使用 MKMapViewDelegate 与 CLLocationManagerDelegate 进行位置更新

问题描述 投票:0回答:1

我想在收到用户的(一次)位置更新后使用

setRegion(_:animated:)
设置 MKMapView 的可见区域。

为了接收用户的位置,我知道有两种不同的方法:

  1. 使用

    MKMapViewDelegate
    及其委托方法
    func mapView(_ mapView: MKMapView, didUpdate userLocation: MKUserLocation)
    。假设我的 Delegate 类设置为我可以访问我想要操作的
    MKMapView
    实例,我可以做类似的事情

    func mapView(_ mapView: MKMapView, didUpdate userLocation: MKUserLocation) {
         let region = MKCoordinateRegion(
             center: CLLocationCoordinate2D(latitude: userLocation.coordinate.latitude, longitude: userLocation.coordinate.longitude),
             span: MKCoordinateSpan(latitudeDelta: 0.05, longitudeDelta: 0.05)
         )
    
         mapView.setRegion(region, animated: true)
    }
    

然而,这里的问题是方法

func mapView(_ mapView: MKMapView, didUpdate userLocation: MKUserLocation)
被不断调用,而我从未请求过任何更新。由于我只想执行一次,因此这对我不起作用。

  1. 在使用
    CLLocationManagerDelegate
    明确请求用户进行一次性位置更新后,使用 locationManager.requestLocation() 并监听
    locationManager(_:didUpdateLocations:)
    中的变化。这个方法只按预期被调用一次,但在这里我不再有任何对我的
    MKMapView
    实例的引用来调用
    setRegion(_:animated:)

所以我的问题是:为什么 1. 中的

func mapView(_ mapView: MKMapView, didUpdate userLocation: MKUserLocation)
被多次调用,这是否显而易见,还是我的代码中可能存在一些设置错误?

我是否应该使用方法 2. 来实现我想要的目标,如果是的话,获得我的

MKMapView
参考的最佳实践是什么?

ios swift mapkit core-location
1个回答
0
投票

我建议第二种方法。

在这种情况下,

Delegation pattern
将为您提供帮助。这种模式使代码松耦合,并且易于维护。或者,您可以使用
Closure
作为回调。

  1. 更新你的 LocationManager 持有者类(假设你有这个,或者你可以使用你自己实现的逻辑)
protocol IYourLocationManangerWrapperClass {
    func onLocationUpdated(location: CLLocation)
}

class YourLocationManangerWrapperClass {
    
    // This kind of feature should be unique in your project scope.
    // So, singleton is a nice choice.
    static let current = YourLocationManangerWrapperClass()
    
    private var locationManager: CLLocationManager!

    // Call this at beginning of your app lifecycle
    // Ex: in AppDelegate.didFinishLaunching....
    static func initialize() {
        let new = CLLocationManager.init()
        new.delegate = self
        current.locationManager = new
    }
    
    // The delegate of your Wrapper class to allow another object can access to Location Service
    private weak var delegate: IYourLocationManangerWrapperClass?
    
    func setDelegate(_ value: IYourLocationManangerWrapperClass) {
        self.delegate = value
    }
    
    func requestLocation() {
        self.locationManager.requestLocation()
    }
}

extension YourLocationManangerWrapperClass: CLLocationManagerDelegate {
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        if let last = locations.last {
            self.delegate?.onLocationUpdated(location: last)
        }
    }
}
  1. 其次,更新包含 MKMapView 的视图
class YourClassThatHasMKMapView {
    @IBOutlet weak var mapView: MKMapView!
    
    func setup() {
        // Connect your MapView container instance here
        YourLocationManangerWrapperClass.current.setDelegate(self)
        
        // Then request location once by calling this
        YourLocationManangerWrapperClass.current.requestLocation()
    }
    
}

extension YourClassThatHasMKMapView: IYourLocationManangerWrapperClass {
    func onLocationUpdated(location: CLLocation) {
        
        // Switch to main thread for UI updating
        DispatchQueue.main.async {
            // Your MKCoordinateRegion setup here ...
            self.mapView.setRegion(MKCoordinateRegion, animated: Bool)
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.