用户在地图视图中。当在地图上的某个地方长按时,会触发以下函数来设置新的注释,包括正确的标题。
func action(gestureRecognizer: UIGestureRecognizer) {
if gestureRecognizer.state == UIGestureRecognizerState.Began {
var newTouch: CGPoint = gestureRecognizer.locationInView(self.mapView)
var newCoordinate: CLLocationCoordinate2D = mapView.convertPoint(newTouch, toCoordinateFromView: self.mapView)
var newLocation = CLLocation(latitude: newCoordinate.latitude, longitude: newCoordinate.longitude)
var newAnnotation = MKPointAnnotation()
newAnnotation.coordinate = newCoordinate
CLGeocoder().reverseGeocodeLocation(newLocation, completionHandler: {(placemarks, error) in
if error != nil { println(error) }
let p: CLPlacemark = placemarks[0] as CLPlacemark
var thoroughfare: String? = p.thoroughfare
var subThoroughfare: String? = p.subThoroughfare
if p.thoroughfare == nil || p.subThoroughfare == nil {
var date = NSDate()
newAnnotation.title = "Added \(date)"
} else {
newAnnotation.title = thoroughfare! + " " + subThoroughfare!
}
})
self.mapView.addAnnotation(newAnnotation)
self.mapView.selectAnnotation(newAnnotation, animated: true)
places.append(["name":"\(newAnnotation.title)", "lat":"\(newCoordinate.latitude)", "lon":"\(newCoordinate.longitude)"])
}
}
我知道在CLGeocoder块中保留最后三行代码(闭包?)时工作正常。但是,如果我将它们分开并在})
之后列出它们(或者将一些代码放到另一个线程中)我面临着它运行异步的问题(因为我不明白如何控制异步vs同步)当注释添加到地图并保存到地方时,CLGeocoder尚未设置其标题。
编程的初学者问:实现什么是必要的(disptach_sync ...-某事)所以最后一行代码等待CLGeocoder块完成?我还没有设法以正确的方式实现这个命令......
答案 0 :(得分:5)
您正确地指出,当您将这三行放在地理编码器的闭包中时,它会起作用。所以,你应该做到这一点:利用这种异步模式,将所有依赖于地理编码过程的东西放在闭包中。
在回答您的问题时,是的,有些模式可以使异步任务以同步方式运行。例如,您可以使用调度组或调度信号量。但是你从IBAction
调用它,它在主线程上运行。你永远不想阻止主线程。因此,任何使这个地理编码请求从主线程同步运行的尝试都是不明智的。
此外,上面的过程由于reverseGeocodeLocation
的皱纹而变得复杂:具体来说,闭包本身在主线程上运行,所以如果你阻塞主线程等待闭包完成,应用程序将僵局。因此,上述模式甚至不能与reverseGeocodeLocation
一起使用(如果从主线程完成)。
最重要的是,您应该接受异步模式,将依赖代码保留在闭包内。
顺便说一下,你的例子很简单,你只需要将你想要执行的代码放在地理编码器的闭包中。但是,如果您想要两个单独的函数,例如,一个用于返回注释的地理编码,另一个函数用于将注释添加到地图,该怎么办?也许你期待的是:
func handleLongPress(gesture: UILongPressGestureRecognizer) {
if gesture.state == .Began {
let location = gesture.locationInView(self.mapView)
let annotation = geocodeLocationInMapView(self.mapView, location: location)
addAnnotationToMapView(self.mapView, annotation: annotation)
}
}
然后问题是你将如何使用第二个函数addAnnotationToMapView
,等待第一个函数geocodeLocationInMapView
完成。同样,答案是“你没有。”相反,就像Apple使用异步方法一样,您将使用完成块模式:
func handleLongPress(gesture: UILongPressGestureRecognizer) {
if gesture.state == .Began {
let location = gesture.locationInView(self.mapView)
geocodeLocationInMapView(self.mapView, location: location) { annotation, error in
guard annotation != nil && error == nil else {
print("error = \(error)")
return
}
self.addAnnotationToMapview(self.mapView, annotation: annotation!)
}
}
}
在这种情况下,geocodeLocationInMapView
可能如下所示:
func geocodeLocationInMapView(mapView: MKMapView, location: CGPoint, completion: (MKAnnotation?, NSError?) -> ()) {
let coordinate = mapView.convertPoint(location, toCoordinateFromView: mapView)
let location = CLLocation(latitude: coordinate.latitude, longitude: coordinate.longitude)
CLGeocoder().reverseGeocodeLocation(location) { placemarks, error in
guard placemarks != nil && error == nil else {
completion(nil, error)
return
}
if let placemark = placemarks!.first {
let annotation = MKPointAnnotation()
annotation.coordinate = coordinate
let thoroughfare = placemark.thoroughfare
let subThoroughfare = placemark.subThoroughfare
switch (thoroughfare, subThoroughfare) {
case (nil, nil):
annotation.title = "Added \(NSDate())"
case (_, nil):
annotation.title = thoroughfare
default:
annotation.title = thoroughfare! + " " + subThoroughfare!
}
completion(annotation, nil)
}
}
}
addAnnotationToMapview
可能看起来像:
func addAnnotationToMapview(mapView: MKMapView, annotation: MKAnnotation) {
mapView.addAnnotation(annotation)
mapView.selectAnnotation(annotation, animated: true)
places.append(["name":"\(annotation.title)", "lat":"\(annotation.coordinate.latitude)", "lon":"\(annotation.coordinate.longitude)"])
}
诚然,这是一个人为的例子,我建议你像我在这个答案开头所描述的那样处理你的IBAction
。但是在回答更广泛的问题“如何让函数A等到函数B完成”之后,你可以使用这里概述的完成块模式。