我想在收到用户的(一次)位置更新后使用
setRegion(_:animated:)设置
MKMapView
的可见区域。
为了接收用户的位置,我知道有两种不同的方法:
使用
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)
被不断调用,而我从未请求过任何更新。由于我只想执行一次,因此这对我不起作用。
CLLocationManagerDelegate
明确请求用户进行一次性位置更新后,使用 locationManager.requestLocation()
并监听 locationManager(_:didUpdateLocations:)中的变化。这个方法只按预期被调用一次,但在这里我不再有任何对我的
MKMapView
实例的引用来调用 setRegion(_:animated:)
。所以我的问题是:为什么 1. 中的
func mapView(_ mapView: MKMapView, didUpdate userLocation: MKUserLocation)
被多次调用,这是否显而易见,还是我的代码中可能存在一些设置错误?
我是否应该使用方法 2. 来实现我想要的目标,如果是的话,获得我的
MKMapView
参考的最佳实践是什么?
我建议第二种方法。
在这种情况下,
Delegation pattern
将为您提供帮助。这种模式使代码松耦合,并且易于维护。或者,您可以使用 Closure
作为回调。
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)
}
}
}
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)
}
}
}