Swift基于CoreLocation框架获取设备的当前地理位置
Swift基于CoreLocation框架获取设备的当前地理位置

Swift基于CoreLocation框架获取设备的当前地理位置

获取当前地理位置代码

下面的代码是是一个基于 CoreLocation 框架的自定义位置获取工具类,用于在应用程序中获取设备的当前地理位置。

import CoreLocation

class LocationFetcher: NSObject, CLLocationManagerDelegate {
    let manager = CLLocationManager()
    var lastKnownLocation: CLLocationCoordinate2D?

    override init() {
        super.init()
        manager.delegate = self
    }

    func start() {
        manager.requestWhenInUseAuthorization()
        manager.startUpdatingLocation()
    }

    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        lastKnownLocation = locations.first?.coordinate
    }
}

代码解析

1、定义 LocationFetcher 类

继承自 NSObject 并实现了 CLLocationManagerDelegate 协议。

这个类封装了位置服务的启动、授权请求以及位置更新逻辑。

2、manager和lastKnownLocation属性

let manager = CLLocationManager()
var lastKnownLocation: CLLocationCoordinate2D?

manager: CLLocationManager 实例,用于管理设备的定位服务。

lastKnownLocation: 存储最近一次获取到的位置(CLLocationCoordinate2D 类型)。

3、init 方法

初始化时,将当前实例设置为 manager 的代理,以接收定位回调。

4、start 方法

请求用户授权访问位置数据 (requestWhenInUseAuthorization)。

启动位置更新 (startUpdatingLocation),后台会不断获取设备的地理位置。

5、locationManager(_:didUpdateLocations:) 回调

当 CLLocationManager 获取到新的位置信息时,会调用此回调。

locations 是一个 CLLocation 数组,取数组中的第一个位置(最新位置)并存储到 lastKnownLocation。

如何调用该代码

以下是如何在应用中使用 LocationFetcher 类的示例:

1、初始化和启动位置服务

let locationFetcher = LocationFetcher()

func fetchLocation() {
    locationFetcher.start() // 启动位置服务
}

2、获取位置

在需要获取位置时,可以访问 lastKnownLocation:

if let location = locationFetcher.lastKnownLocation {
    print("Latitude: \(location.latitude), Longitude: \(location.longitude)")
} else {
    print("Location not available yet")
}

实际应用

在调取locationFetcher.start()方法后,会弹出获取当前地理位置的请求。

在显示视图中,可以根据locationFetcher.lastKnownLocation获取到最近的一次定位。

注意事项

1、权限声明

在 Info.plist 中添加定位服务权限说明:

Privacy - Location When In Use Usage Description

如果不知道Info.plist文件,可以查看《Xcode项目Info.plist文件位置

2、电池消耗

使用定位服务可能会影响设备的电池寿命。使用 startUpdatingLocation 时应注意停止不必要的更新(例如在获取一次位置后调用 stopUpdatingLocation)。

manager.stopUpdatingLocation()

3、精度要求

如果需要更高的精度,可以设置 CLLocationManager 的 desiredAccuracy 属性,例如:

manager.desiredAccuracy = kCLLocationAccuracyBest

相关知识可以进一步阅读《Swift高精度定位desiredAccuracy

4、定位失败: 未能完成操作。(kCLErrorDomain错误1。)

如果Xcode输出定位保持,可能是没有配置正确Info.plist方法。

也可能是用户手动拒绝过权限,就需要提示用户去到iPhone的设置-隐私-定位服务里重新打开权限。

在Xcode模拟器中,定位默认为None(无位置),需要在菜单栏选择:

Features > Location > Apple

或者选择一个模拟路径(City Run / City Bicycle / Freeway Drive)。

适用场景

需要获取设备的当前位置,例如用于地图导航、地理打卡等。

用于需要持续或实时获取位置的功能,但要注意隐私问题和用户授权。

模拟器获取地理位置

Xcode模拟器默认关闭地理位置的获取方式,需要在Xcode模拟器的菜单栏-Features-Location控制模拟器 App 所接收的地理位置数据。

菜单选项

1、None:不提供任何地理位置信息。App 如果请求位置,可能会得到 kCLLocationCoordinate2DInvalid 或报错,就像设备关掉定位一样。

2、Custom Location…:可以手动输入经纬度(Latitude / Longitude),这样就能让模拟器“假装”自己在那个坐标上。非常适合测试国外城市或某个特定坐标点。

3、City Run:模拟一条在城市中跑步的轨迹。模拟器会随着时间推移不断更新位置,像人慢跑一样移动。

4、City Bicycle Ride:模拟在城市骑自行车的轨迹,速度会比跑步快一些。

5、Freeway Drive:模拟在高速公路上开车的轨迹,速度更快,位置更新更频繁。

6、Apple:直接把模拟位置设在 Apple 总部(加州 Cupertino)。这是默认的“基准点”。

例如,选择Apple后,会输出Apple总部的经纬度。

为什么不能获取真实位置?

因为 Mac 电脑本身没有 iPhone/iPad 那样的 GPS 芯片,模拟器也没有办法直接桥接你 Mac 的 Wi-Fi 定位(而且精度很差)。所以 Xcode 的设计就是:只提供虚拟位置,不会把你的真实地理位置暴露给模拟器。

如果你想在开发时用真实位置测试,有两个办法:

1、把 App 跑在真机上(iPhone/iPad),它就会用设备的 Core Location。

2、在 Mac 上写个脚本或小工具,通过 Xcode 的 GPX 文件(可以模拟轨迹和位置)去动态输入你想要的“假位置”。

参考文章

SwiftUI 100天 – Time for MapKit:https://www.hackingwithswift.com/100/swiftui/78

代码示例

使用stopUpdatingLocation方法的定位代码

import CoreLocation

class LocationFetcher: NSObject, CLLocationManagerDelegate {
    private let manager = CLLocationManager()
    private(set) var lastKnownLocation: CLLocationCoordinate2D?
    
    override init() {
        super.init()
        manager.delegate = self
    }
    
    func start() {
        manager.requestWhenInUseAuthorization() // 请求用户授权
        manager.startUpdatingLocation() // 开始定位
    }
    
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        if let location = locations.first?.coordinate {
            lastKnownLocation = location
            print("获取到的位置: \(location.latitude), \(location.longitude)")
            
            // 获取到位置后,停止更新
            manager.stopUpdatingLocation()
            print("停止位置更新以节省电池寿命")
        }
    }
    
    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
        print("定位失败: \(error.localizedDescription)")
        manager.stopUpdatingLocation() // 出现错误时也应停止更新
    }
}

struct MapView: View {
    let locationFetcher = LocationFetcher()

    var body: some View {
        VStack {
            Button("Start Tracking Location") {
                locationFetcher.start()
            }
        }
    }
}

#Preview {
    MapView()
}

相比之前的代码,每次调用start()方法后,只会获取一次定位,然后停止获取。

执行效果

1、点击 “开始定位” 按钮后,应用会请求用户授权访问位置信息。

2、一旦获取到位置信息,会在控制台打印设备的纬度和经度:

获取到的位置: 37.7749, -122.4194
停止位置更新以节省电池寿命
这个类的用途

1、简化定位服务的使用

开发者只需要调用 start() 方法即可启动定位服务。

定位结果和错误都会通过 lastKnownLocation 或控制台日志反馈。

2、适合一次性定位的场景

比如需要获取用户当前位置以进行某些操作(天气查询、地图标记等)。

获取到位置后会自动停止更新,减少电池消耗。

为什么这样做重要?

1、节省电池寿命

持续更新位置会耗费大量电量,尤其是在启用 GPS 的情况下。

不必要的更新会让用户的设备快速耗电,影响用户体验。

2、提升性能

定位服务会占用系统资源,不必要的调用可能降低设备性能。

3、符合最佳实践

合理使用系统资源是 iOS 开发的一个重要原则。

优化实现方式

如果需要多次定位或连续使用,可以通过以下方式调整逻辑:

1、保持定位器状态

改为不在每次成功获取位置后立即停止,而是根据需要手动控制停止:

func stop() {
    manager.stopUpdatingLocation()
    print("手动停止位置更新")
}

调用 locationFetcher.stop() 方法手动停止更新。

2、状态标志优化

使用一个标志位来控制是否需要持续定位,例如:

var shouldKeepUpdating = false

func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
    if let location = locations.first?.coordinate {
        lastKnownLocation = location
        print("获取到的位置: \(location.latitude), \(location.longitude)")
        
        if !shouldKeepUpdating {
            manager.stopUpdatingLocation()
            print("停止位置更新")
        }
    }
}
   

如果您认为这篇文章给您带来了帮助,您可以在此通过支付宝或者微信打赏网站开发者。

欢迎加入我们的 微信交流群QQ交流群,交流更多精彩内容!
微信交流群二维码 QQ交流群二维码

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注