[iOS] 위치 자동완성 검색

승아·2021년 7월 5일
3

Searching for Nearby Points of Interest 을 참고하였습니다.

✅⠀MapKit을 활용하여 위치 자동완성 검색 후 해당 위치의 위도, 경도 가져오기

1. TableView 와 SearchBar를 생성해줍니다.

Storyboard에서 TableView와 SearchBar를 아래 사진과 같이 생성해주세요.

tableview에 datasoruce와 delegate 설정해주시고 searchbar에도 delegate를 설정해주세요.

tableviewcell은 이런식으로 설정해주었습니다.

class Cell: UITableViewCell{
    @IBOutlet weak var titleLabel: UILabel!
    @IBOutlet weak var subtitleLabel: UILabel!
}

2. 변수 선언

우선 MapKit을 import 해준 후 tableView와 searchbar를 IBOutlet으로 연결해주었습니다.

import UIKit
import MapKit

class ViewController: UIViewController {

    @IBOutlet weak var tableView: UITableView!
    @IBOutlet weak var searchbar: UISearchBar!
    

구현되는 순서는

  1. SearchBar에 입력된 문자열을 searchCompleter에게 넘긴다.
  2. 해당 문자열을 포함하는 Location 정보를 completerResults에 담는다.
  3. tableView에 completerResults를 표현한다.
  4. tableView에 cell을 선택하면 해당 Location의 정보(위도, 경도 등)을 가져온다.

필요한 변수는

  • searchCompleter: MKLocalSearchCompleter // 검색을 도와주는 변수
  • searchRegion: MKCoordinateRegion // 검색 지역 범위를 결정하는 변수
  • completerResults: [MKLocalSearchCompletion] // 검색한 결과를 담는 변수
  • places: MKMapItem // tableView에서 선택한 Location의 정보를 담는 변수
  • localSearch: MKLocalSearch? // tableView에서 선택한 Location의 정보를 가져오는 변수
    private var searchCompleter: MKLocalSearchCompleter?
    private var searchRegion: MKCoordinateRegion = MKCoordinateRegion(MKMapRect.world)
    var completerResults: [MKLocalSearchCompletion]?
   
    private var places: MKMapItem? {
        didSet {
            tableView.reloadData()
        }
    }
    
    private var localSearch: MKLocalSearch? {
        willSet {
            // Clear the results and cancel the currently running local search before starting a new search.
            places = nil
            localSearch?.cancel()
        }
    }

searchCompleter 정의

resultType은 검색할 유형을 나타냅니다.

  • address : 주소 검색
  • pointOfInterest : 건물과 같은 특정 지점 검색
  • query : 모든 결과 검색

region은 검색할 지역 범위를 나타냅니다.

Completer 개체는 수명이 긴 개체이므로 강력한 참조를 하기 때문에viewDidDisappear에서 참조를 해제 해줍니다.

    override func viewDidLoad() {
        super.viewDidLoad()
        
        searchCompleter = MKLocalSearchCompleter()
        searchCompleter?.delegate = self
       	searchCompleter?.resultTypes = .address // 혹시 값이 안날아온다면 이건 주석처리 해주세요
        searchCompleter?.region = searchRegion
        
      	searchbar.delegate = self
        tableView.dataSource = self
        tableView.delegate = self
    }
    
    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        searchCompleter = nil
    }

3. SearchBar Delegate 구현

searchCompleter?.queryFragment에 SearchBar에 입력된 값을 넘겨줍니다.

extension ViewController: UISearchBarDelegate {
    func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
        if searchText == "" {
            completerResults = nil
        }
        
        searchCompleter?.queryFragment = searchText
    }
}

4. MKLocalSearchCompleterDelegate 구현

searchCompleter?.queryFragment에 들어온 값을 토대로 Location을 검색합니다.

func completerDidUpdateResults(_ completer: MKLocalSearchCompleter)에 completer.results를 통해 검색한 결과를 completerResults에 담습니다.

func completer(_ completer: MKLocalSearchCompleter, didFailWithError error: Error)를 통해 에러를 확인합니다.

extension ViewController: MKLocalSearchCompleterDelegate {
    func completerDidUpdateResults(_ completer: MKLocalSearchCompleter) {
        completerResults = completer.results 
        tableView.reloadData()
    }
    
    func completer(_ completer: MKLocalSearchCompleter, didFailWithError error: Error) {
        if let error = error as NSError? {
            print("MKLocalSearchCompleter encountered an error: \(error.localizedDescription). The query fragment is: \"\(completer.queryFragment)\"")
        }
    }
}

5. UITableViewDataSource 구현

completerResult의 값을 tableView에 나타냅니다.

title은 관심 지점과 관련된 제목 문자열입니다.
subtitle은 관심 지점과 관련된 부제목 (있는 경우)입니다.

extension ViewController: UITableViewDataSource{
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return completerResults?.count ?? 0
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        guard let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as? Cell else { return UITableViewCell()}
        
        if let suggestion = completerResults?[indexPath.row] {
            cell.titleLabel.text = suggestion.title
            cell.subtitleLabel.text = suggestion.subtitle
        }
        return cell
    }
}

이렇게 검색부분은 끝났습니다 😱

그럼 이제 tableView의 Cell을 선택하여 Location의 정보를 가져와 봅시다 !!

6. UITableViewDelegate 구현

1. tableView에서 선택한 completerResults 값을 가져옵니다.

extension ViewController: UITableViewDelegate{
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true) // 선택 표시 해제
        
        if let suggestion = completerResults?[indexPath.row] {
            search(for: suggestion)
        }
    }
}

2. 1번의 값을 토대로 MKLocalSearch.Request를 생성합니다.

private func search(for suggestedCompletion: MKLocalSearchCompletion) {
    let searchRequest = MKLocalSearch.Request(completion: suggestedCompletion)
    search(using: searchRequest)
}

3. 2번의 값을 토대로 MKLocalSearch를 생성합니다.

4. MKLocalSearch의 start함수를 사용하여 검색을 시작하여 정보를 가져옵니다.

private func search(using searchRequest: MKLocalSearch.Request) {
    // 검색 지역 설정
    searchRequest.region = searchRegion
    
    // 검색 유형 설정
    searchRequest.resultTypes = .pointOfInterest
    // MKLocalSearch 생성
    localSearch = MKLocalSearch(request: searchRequest)
    // 비동기로 검색 실행
    localSearch?.start { [unowned self] (response, error) in
        guard error == nil else {
            return
        }
        // 검색한 결과 : reponse의 mapItems 값을 가져온다.
        self.places = response?.mapItems[0]
        
        print(places?.placemark.coordinate) // 위경도 가져옴
    }
}

그 외 MKMapItem이 갖고 있는 정보

끝이났습니다 👏🏻⠀검색한 결과를 tableView에 표현하는건 어렵지 않지만 선택한 Location의 정보를 가져오는 부분이 조금 헷갈리긴하네요 ..

MKLocalSearchCompletion -> MKLocalSearch.Request -> MKLocalSearch의 과정을 거쳐 값을 가져와야 되는 ... 😅

7개의 댓글

comment-user-thumbnail
2021년 7월 14일

와 장소 검색 진짜 열심히 찾고 있었는데 너무 감사합니다!!!!

1개의 답글
comment-user-thumbnail
2022년 2월 27일

승아님! 덕분에 공부가 되었어요 감사합니다 🥰

1개의 답글
comment-user-thumbnail
2022년 3월 15일

메일로 질문좀 해도 될까요??

1개의 답글
comment-user-thumbnail
2022년 8월 9일

무슨 일인지 똑같이 했음에도 cell이 나타나지 않습니다ㅠㅠㅠㅠ 왜 이럴까요?
searchBar만 뜨고 문자열 입력해도 밑에가 나타나지 않습니다

답글 달기