Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Search for Place Names (City/Town) Using MKLocalSearchCompleter

I'm trying to build a autocomplete textfield which needs to display the city/town name only.

So what I want to do is that when someone enters

Am it will show

Amsterdam
Amstelveen

So it will only display the actual city names and nothing beyond that. It doesn't need to take places neighbourhoods etc into consideration.

I've applied a filter already but this doesn't fix it.

lazy var searchCompleter: MKLocalSearchCompleter = {
        let sC = MKLocalSearchCompleter()
        sC.delegate = self
        sC.filterType = .locationsOnly
        return sC
    }()

func completerDidUpdateResults(_ completer: MKLocalSearchCompleter) {
        self.searchSource = completer.results.map { $0.title }
        DispatchQueue.main.async {
            for result in self.searchSource! {
                print(result)
            }
        }
    }

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

Does anyone know if it's possible to achieve what I want?

like image 621
NoSixties Avatar asked Apr 19 '17 10:04

NoSixties


1 Answers

I've been looking for a way to do this in one of my apps, but MKLocalSearchCompletion does not seem to be the ideal tool for this. Using Google's Map API or just a local database of city names might be simpler.

Another option within the iOS native frameworks would be to make use of the related MKLocalSearchRequest and extracting the 'locality' field, which most closely corresponds to city/town. More information about going down this route is provided in this post:

How to extract country and city from MKLocalSearchCompletion?

Having said that, I did make some headway using just MKLocalSearchCompletion, by parsing the title property in returned results to check for a 'comma' character. The presence of a comma indicates that the entire string up to the first comma is a town, city, or state. The simple example below takes the textfield input and returns only the filtered results in a tableview.

I have to note that it seems to work well for cities in the United States because the MKLocalSearchCompletion database seems to be much more complete for that region. Some international cities do not show, as results do not follow the same 'comma' format which this method utilizes.

import UIKit
import MapKit

class ViewController: UIViewController, MKLocalSearchCompleterDelegate, UITableViewDelegate, UITableViewDataSource {

    var completer = MKLocalSearchCompleter()

    var completionResults: [MKLocalSearchCompletion] = []

    var cityResults: [String] = [] {
        didSet {
            citySearchTable.reloadData()
        }
    }

    @IBOutlet weak var citySearchTable: UITableView!

    @IBAction func cityTextChanged(_ sender: UITextField) {
        completer.queryFragment = sender.text!
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.

        let coordUSA = CLLocationCoordinate2DMake(39.999733,-98.678503);

        completer.region = MKCoordinateRegion(center: coordUSA, latitudinalMeters: 1, longitudinalMeters: 1)

        completer.delegate = self
        citySearchTable.delegate = self
        citySearchTable.dataSource = self
    }

    func completerDidUpdateResults(_ completer: MKLocalSearchCompleter) {
        completionResults = completer.results
        completionResults = completionResults.filter({ (result) -> Bool in
            return result.title != ""
        })
        if completionResults.count > 0 {
            var newResults: [String] = []
            for result in completionResults {
                if result.title.contains(",") {
                    let splitByComma = result.title.components(separatedBy: ",")
                    if splitByComma.count > 0 {
                        if !newResults.contains(splitByComma[0]) {
                            newResults.append(splitByComma[0])
                        }
                    }
                }
            }
            if newResults.count > 0 {
                cityResults = newResults
            }
        }
    }

    func completer(_ completer: MKLocalSearchCompleter, didFailWithError error: Error) {
        //
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return cityResults.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = citySearchTable.dequeueReusableCell(withIdentifier: "cell")!
        cell.textLabel?.text = cityResults[indexPath.row]
        cell.textLabel?.adjustsFontSizeToFitWidth = true
        return cell
    }

}

I know this post is a couple of years old, however it did come up in my recent research, and is relevant to my current work, so I thought I would post what I came up with.

like image 73
Wattholm Avatar answered Nov 01 '22 07:11

Wattholm