Ios 正确使用MKLocalSearch CompletionHandler
我正在使用mapkit制作一个应用程序,帮助用户找到附近吸引他们的餐馆(除其他外),并尝试使用MKLocalSearch。我已声明myMapItems,这是一个MKMapItem数组,并试图将其设置为与MKLocalSearch的结果相同。打印MKLocalSearch的结果时,会向我提供所有十个MKMAPItem,但当将myMapItems数组设置为与结果相等时,控制台会告诉我myMapItems为零。因此:Ios 正确使用MKLocalSearch CompletionHandler,ios,swift,mapkit,mklocalsearch,Ios,Swift,Mapkit,Mklocalsearch,我正在使用mapkit制作一个应用程序,帮助用户找到附近吸引他们的餐馆(除其他外),并尝试使用MKLocalSearch。我已声明myMapItems,这是一个MKMapItem数组,并试图将其设置为与MKLocalSearch的结果相同。打印MKLocalSearch的结果时,会向我提供所有十个MKMAPItem,但当将myMapItems数组设置为与结果相等时,控制台会告诉我myMapItems为零。因此: var myMapItems:[MKMapItem]? 然后,在将“区域”定义为协调
var myMapItems:[MKMapItem]?
然后,在将“区域”定义为协调区域后
let request = MKLocalSearchRequest()
request.naturalLanguageQuery = "Restaurants"
request.region = region!
let search = MKLocalSearch(request: request)
search.start { (response, error) in
print(response?.mapItems)
self.myMapItems = response?.mapItems
}
因此,当我按下运行此代码的按钮时,控制台将打印response.mapItems
,但无法将我先前声明的mapItems设置为与搜索结果相等
更详细的代码:
import UIKit
import MapKit
class MapViewController: UIViewController, CLLocationManagerDelegate {
@IBOutlet weak var mapView: MKMapView!
let locationManager = CLLocationManager()
@IBOutlet weak var slider: UISlider!
@IBAction func searchButtonPressed(_ sender: Any) {
search()
print(self.mapItems)
}
var mapItems: [MKMapItem] = []
func search() {
var region: MKCoordinateRegion?
let userLocation = CLLocation(latitude: (locationManager.location?.coordinate.latitude)!, longitude: (locationManager.location?.coordinate.longitude)!)
//Function for translating a CLLocationCoordinate2D by x meters
func locationWithBearing(bearing:Double, distanceMeters:Double, origin:CLLocationCoordinate2D) -> CLLocationCoordinate2D {
let distRadians = distanceMeters / (6372797.6)
let rbearing = bearing * Double.pi / 180.0
let lat1 = origin.latitude * Double.pi / 180
let lon1 = origin.longitude * Double.pi / 180
let lat2 = asin(sin(lat1) * cos(distRadians) + cos(lat1) * sin(distRadians) * cos(rbearing))
let lon2 = lon1 + atan2(sin(rbearing) * sin(distRadians) * cos(lat1), cos(distRadians) - sin(lat1) * sin(lat2))
return CLLocationCoordinate2D(latitude: lat2 * 180 / Double.pi, longitude: lon2 * 180 / Double.pi)
}
//Function for generating the search region within user's specified radius
func generateRandomSearchRegionWithinSpecifiedRadius() {
//Get user location
let userCurrentLocation = CLLocationCoordinate2DMake(userLocation.coordinate.latitude, userLocation.coordinate.longitude)
//Set randomLocationWithinSearchRadius to the user's current location translated in a randomly selected direction by a distance within x miles as specified by the user's slider
let randomLocationWithinSearchRadius = locationWithBearing(bearing: Double(arc4random_uniform(360)), distanceMeters: Double(arc4random_uniform(UInt32(slider.value * 1609.34))), origin: userCurrentLocation)
//Set region to an MKCoordinateRegion with this new CLLocationCoordinate2D as the center, searching within 3 miles
region = MKCoordinateRegionMakeWithDistance(randomLocationWithinSearchRadius, 4828.03, 4828.03)
print("\nRegion:\n Lat: \(region?.center.latitude ?? 0)\n Long: \(region?.center.longitude ?? 0)\n Radius: \(round((region?.span.latitudeDelta)! * 69))")
}
//Generate the random searching region within specified radius
generateRandomSearchRegionWithinSpecifiedRadius()
//Find distance between userLocation and our generated search location
let distanceBetweenLocations = CLLocation(latitude: (region?.center.latitude)!, longitude: (region?.center.longitude)!).distance(from: CLLocation(latitude: (userLocation.coordinate.latitude), longitude: (userLocation.coordinate.longitude)))
//Compare distance between locations with that requested by user's slider
print("\n Distance between locations: \(distanceBetweenLocations / 1609.34)\n Slider value: \(slider.value)\n\n\n")
//If the function generates a location whose distance from the user is further than that requested by the slider, the function will repeat
while distanceBetweenLocations > Double((slider.value) * 1609.34) {
generateRandomSearchRegionWithinSpecifiedRadius()
}
let request = MKLocalSearchRequest()
request.naturalLanguageQuery = "Restaurants"
request.region = region!
let search = MKLocalSearch(request: request)
search.start { (response, error) in
//print(response?.mapItems)
for item in (response?.mapItems)! {
self.mapItems.append(item)
}
}
}
如果有人需要,全班都可以:
import UIKit
import MapKit
class MapViewController: UIViewController, CLLocationManagerDelegate {
@IBOutlet weak var mapView: MKMapView!
let locationManager = CLLocationManager()
@IBOutlet weak var pickerTextField: UITextField!
let actions = ["A place to eat", "Something fun to do", "A park", "A club or bar"]
@IBOutlet weak var slider: UISlider!
@IBOutlet weak var distanceLabel: UILabel!
@IBOutlet weak var searchButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
pickerTextField.loadDropdownData(data: actions)
// Do any additional setup after loading the view, typically from a nib.
// Core Location Manager asks for GPS location
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.startMonitoringSignificantLocationChanges()
// Check if the user allowed authorization
if (CLLocationManager.authorizationStatus() == CLAuthorizationStatus.authorizedWhenInUse ||
CLLocationManager.authorizationStatus() == CLAuthorizationStatus.authorizedAlways)
{
// set initial location as user's location
let initialLocation = CLLocation(latitude: (locationManager.location?.coordinate.latitude)!, longitude: (locationManager.location?.coordinate.longitude)!)
centerMapOnLocation(location: initialLocation, regionRadius: 1000)
print("Latitude: \(locationManager.location?.coordinate.latitude), Longitude: \(locationManager.location?.coordinate.longitude)")
} else {
print("Failed")
}
let span : MKCoordinateSpan = MKCoordinateSpanMake(0.1, 0.1)
let location : CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: 38.645933, longitude: -90.503613)
let pin = PinAnnotation(title: "Gander", subtitle: "You can get food here", coordinate: location)
mapView.addAnnotation(pin)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func centerMapOnLocation(location: CLLocation, regionRadius: Double) {
let regionRadius = CLLocationDistance(regionRadius)
let coordinateRegion = MKCoordinateRegionMakeWithDistance(location.coordinate,
regionRadius, regionRadius)
mapView.setRegion(coordinateRegion, animated: true)
}
@IBAction func sliderAdjusted(_ sender: Any) {
var int = Int(slider.value)
distanceLabel.text = "\(int) miles"
}
@IBAction func searchButtonPressed(_ sender: Any) {
search()
print(self.mapItems)
//chooseRandomSearchResult(results: self.mapItems!)
}
var mapItems: [MKMapItem] = []
func search() {
var region: MKCoordinateRegion?
let userLocation = CLLocation(latitude: (locationManager.location?.coordinate.latitude)!, longitude: (locationManager.location?.coordinate.longitude)!)
//Function for translating a CLLocationCoordinate2D by x meters
func locationWithBearing(bearing:Double, distanceMeters:Double, origin:CLLocationCoordinate2D) -> CLLocationCoordinate2D {
let distRadians = distanceMeters / (6372797.6)
let rbearing = bearing * Double.pi / 180.0
let lat1 = origin.latitude * Double.pi / 180
let lon1 = origin.longitude * Double.pi / 180
let lat2 = asin(sin(lat1) * cos(distRadians) + cos(lat1) * sin(distRadians) * cos(rbearing))
let lon2 = lon1 + atan2(sin(rbearing) * sin(distRadians) * cos(lat1), cos(distRadians) - sin(lat1) * sin(lat2))
return CLLocationCoordinate2D(latitude: lat2 * 180 / Double.pi, longitude: lon2 * 180 / Double.pi)
}
//Function for generating the search region within user's specified radius
func generateRandomSearchRegionWithinSpecifiedRadius() {
//Get user location
let userCurrentLocation = CLLocationCoordinate2DMake(userLocation.coordinate.latitude, userLocation.coordinate.longitude)
//Set randomLocationWithinSearchRadius to the user's current location translated in a randomly selected direction by a distance within x miles as specified by the user's slider
let randomLocationWithinSearchRadius = locationWithBearing(bearing: Double(arc4random_uniform(360)), distanceMeters: Double(arc4random_uniform(UInt32(slider.value * 1609.34))), origin: userCurrentLocation)
//Set region to an MKCoordinateRegion with this new CLLocationCoordinate2D as the center, searching within 3 miles
region = MKCoordinateRegionMakeWithDistance(randomLocationWithinSearchRadius, 4828.03, 4828.03)
print("\nRegion:\n Lat: \(region?.center.latitude ?? 0)\n Long: \(region?.center.longitude ?? 0)\n Radius: \(round((region?.span.latitudeDelta)! * 69))")
}
//Generate the random searching region within specified radius
generateRandomSearchRegionWithinSpecifiedRadius()
//Find distance between userLocation and our generated search location
let distanceBetweenLocations = CLLocation(latitude: (region?.center.latitude)!, longitude: (region?.center.longitude)!).distance(from: CLLocation(latitude: (userLocation.coordinate.latitude), longitude: (userLocation.coordinate.longitude)))
//Compare distance between locations with that requested by user's slider
print("\n Distance between locations: \(distanceBetweenLocations / 1609.34)\n Slider value: \(slider.value)\n\n\n")
//If the function generates a location whose distance from the user is further than that requested by the slider, the function will repeat
while distanceBetweenLocations > Double((slider.value) * 1609.34) {
generateRandomSearchRegionWithinSpecifiedRadius()
}
let request = MKLocalSearchRequest()
request.naturalLanguageQuery = "Restaurants"
request.region = region!
let search = MKLocalSearch(request: request)
search.start { (response, error) in
//print(response?.mapItems)
for item in (response?.mapItems)! {
self.mapItems.append(item)
}
}
func chooseRandomSearchResult(results: [MKMapItem]) -> MKMapItem {
let numberOfItems = results.count
let randomNumber = Int(arc4random_uniform(UInt32(numberOfItems)))
return results[randomNumber]
}
}问题在于search.start是异步的,它将启动请求并在结果完成之前返回。假设您需要在完成处理程序中工作。 替换: To:(删除结果的用法,因为它们还不存在,实际上搜索已启动,但尚未返回) 并在回调中执行实际工作:
//Still inside search()
search.start { (response, error) in
//Executed after search() has already returned
print(response?.mapItems)
self.mapItems = response?.mapItems
chooseRandomSearchResult(results: self.mapItems!)
}
//Still inside search()
如您所见,代码标记为:
//已返回search()后执行
始终在//仍在搜索()之后执行
即使它在函数中位于它之前
(从iOS 11.x开始,文档保证MKLocalSearch.start的完成处理程序将在主线程上)您在控制台上打印的是哪一行?@MitchellCurrie in search.start,当我按下按钮时,它会在函数中被调用。我还让它将self.myMapItems设置为与正在打印的结果相同,但在这样做之后,我让它打印myMapItems,并且控制台显示为nil。此外,每当我尝试使用myMapItems的内容时,应用程序都会崩溃,因为它被设置为nil。您可以发布更多代码,包括类和任何委托方法吗implemented@MitchellCurrie我增加了课堂的大部分内容,删掉了我认为与问题无关的大部分内容。按下my UIButton时会调用search()函数,search()开头的大部分代码只是通过用户给定的变量生成“region”(用于搜索请求)。奇怪的是,你说它在尝试使用mapItems时崩溃,然而,这是一个可选的,我看不到你强制展开任何地方。无论如何,更安全的选择是始终使用空数组。在search.start中调用chooseRandomSearchResult(results:self.mapItems!)而不是searchPressed()似乎可以做到这一点。非常感谢!很好,记住闭包可以是异步的
@IBAction func searchButtonPressed(_ sender: Any) {
search()
}
//Still inside search()
search.start { (response, error) in
//Executed after search() has already returned
print(response?.mapItems)
self.mapItems = response?.mapItems
chooseRandomSearchResult(results: self.mapItems!)
}
//Still inside search()