如何在MapKit和Swift 5中搜索城市?

3

我正在尝试构建一个简单的天气应用程序,但在尝试实现搜索城市功能时遇到了困难。我已经成功实现了搜索功能以返回CLPlacemarks列表,但其中包括许多兴趣点(例如餐馆、街道名称等),这使得结果非常混乱。是否有办法将结果限制为仅包含该名称的城市?以下是我的代码:

func updateSearchResults(for searchController: UISearchController) {

    var searchText = searchController.searchBar.text
    
    request.naturalLanguageQuery = searchText
    localSearch = MKLocalSearch(request: request)
    localSearch?.start { (searchResponse, _) in
        guard let items = searchResponse?.mapItems else {
            return
        }
        self.placemarks = [CLPlacemark]()
        for pm in items {
            self.placemarks.append(pm.placemark)
        }
    }
}

1
您可以在请求中添加以下行;request.resultTypes = .address。这将仅设置地址为您的结果。 - user2023106
3个回答

4

使用 CombineSwiftUI (Swift 5.3) 进行测试。

这不是完美的解决方案,但你只能得到城市和国家:

MKLocalSearchCompleter 的设置:

searchCompleter = MKLocalSearchCompleter()    
searchCompleter.delegate = self
searchCompleter.region = MKCoordinateRegion(.world)
searchCompleter.resultTypes = MKLocalSearchCompleter.ResultType([.address])

在委托方法中添加的代码:

func completerDidUpdateResults(_ completer: MKLocalSearchCompleter) {
    
    let searchResults = self.getCityList(results: completer.results)
    
    print(searchResults)
}

func completer(_ completer: MKLocalSearchCompleter, didFailWithError error: Error) {
    
    print(error.localizedDescription)
}

获取城市信息:

func getCityList(results: [MKLocalSearchCompletion]) -> [(city: String, country: String)]{
    
    var searchResults: [(city: String, country: String)] = []
    
    for result in results {
        
        let titleComponents = result.title.components(separatedBy: ", ")
        let subtitleComponents = result.subtitle.components(separatedBy: ", ")
        
        buildCityTypeA(titleComponents, subtitleComponents){place in
            
            if place.city != "" && place.country != ""{
                
                searchResults.append(place)
            }
        }
        
        buildCityTypeB(titleComponents, subtitleComponents){place in
            
            if place.city != "" && place.country != ""{
                
                searchResults.append(place)
            }
        }
    }
    
    return searchResults
}

您可以获取两种类型的城市:

func buildCityTypeA(_ title: [String],_ subtitle: [String], _ completion: @escaping ((city: String, country: String)) -> Void){
    
    var city: String = ""
    var country: String = ""
    
    if title.count > 1 && subtitle.count >= 1 {
        
        city = title.first!
        country = subtitle.count == 1 && subtitle[0] != "" ? subtitle.first! : title.last!
    }
    
    completion((city, country))
}

func buildCityTypeB(_ title: [String],_ subtitle: [String], _ completion: @escaping ((city: String, country: String)) -> Void){
    
    var city: String = ""
    var country: String = ""
    
    if title.count >= 1 && subtitle.count == 1 {
        
        city = title.first!
        country = subtitle.last!
    }
    
    completion((city, country))
}


你是在UIViewRepresentable中编写这段代码吗? - Nico Cobelo
不,它是一个外部的Swift文件控制器。 - Matthew Rodríguez

0

我正在寻找一种通过搜索来选择世界各地城市的方法。在考虑了API、构建实际数据搜索顶级城市、使用Search Controller显示搜索结果等之后,这是最快的解决方案:

对我而言可行的方法是仅使用CoreLocation,假设您只想要最接近用户搜索或输入的城市/GPS(Lat Lng)坐标。

对于像“华盛顿特区”这样的用户输入,您可以将该字符串传递到geocodeAddressString函数中:

 @IBAction func entryFieldAction(_ sender: UITextField) {
        
        
        DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
            
            
            let city = sender.text ?? " "
            
 
            if (city == " " || city == "") {
                
                return
            }
            
           
            self.getCoordinate(addressString: city) { coordinate, place, error in
             
                        
                    let lat = coordinate.latitude
                    let lng = coordinate.longitude

                    let city = place?.locality ?? ""
                    let province = place?.administrativeArea ?? ""
                    let country = place?.country ?? ""
                    
                 
                    print(city)
                    // Washington, DC
                    print(lat)
                    // 38.8954381
                    print(lng)
                    //-77.0312812
                    print(city)
                    //Washington
                    print(province)
                    // DC
                    print(country)
                    // United States
             
             
            }
                
             
        }
        
        
    }


  func getCoordinate( addressString : String,
            completionHandler: @escaping(CLLocationCoordinate2D, CLPlacemark?, NSError?) -> Void ) {
        let geocoder = CLGeocoder()
        geocoder.geocodeAddressString(addressString) { (placemarks, error) in
            if error == nil {
                
                if let placemark = placemarks?[0] {
                    
                    let location = placemark.location!
                        
                    completionHandler(location.coordinate, placemark, nil)
                   
                    return
                }
            }
                
            completionHandler(kCLLocationCoordinate2DInvalid, nil, error as NSError?)
        }
    }

所以这将接受用户输入的任何字符串,并尽最大努力为城市、州/省、国家以及GPS(Lat Lng)坐标生成单个结果。它似乎非常好,因为它可以处理各种用户输入,如格式、城市、地址、拼写。

很想听听其他/更好的做法。


0

我正在进行一个研究项目,与此相同。我已经接近了解决方法,但其中有一些令人沮丧的方面。

以下是设置内容:

private var completer: MKLocalSearchCompleter = MKLocalSearchCompleter()

// triggers the search-as-you-type
//
func searchFor(term: String) {
    completer.delegate = self
    completer.region = MKCoordinateRegion(.world)
    completer.pointOfInterestFilter = MKPointOfInterestFilter.excludingAll
    completer.queryFragment = term
}

我将搜索区域设置为全球,然后删除了所有的兴趣点。

在完成函数中,我过滤掉了任何没有逗号的内容。结果通常是城市、州或地区、国家(国家可选)。

let results = completer.results.filter { result in
    guard result.title.contains(",") else { return false }
    return true
}
self.searchTerms = results.map { $0.title }

这对于美国的任何事物都很有效。但是尝试输入“巴黎”或“伦敦”,你会得到“巴黎,德克萨斯州”,因为巴黎在法国只是“巴黎”,原因不明。
然而,如果你检查.subtitle属性,你会发现法国的巴黎有副标题“法国”(为什么这不一致超出了我的理解)。所以我调整了我的completer函数如下:
func completerDidUpdateResults(_ completer: MKLocalSearchCompleter) {
    let results = completer.results.filter { result in
        guard result.title.contains(",") || !result.subtitle.isEmpty else { return false }
        guard !result.subtitle.contains("Nearby") else { return false }
        return true
    }
    self.searchTerms = results.map { $0.title + ($0.subtitle.isEmpty ? "" : ", " + $0.subtitle) }
}

这不太美观,但它运行得相当不错


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接