0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

RxSwiftを使ってMapKitで現在地から場所をサジェストするauto completeを実装するサンプル

Posted at

現在地付近のの施設を検索できるサンプルになります

MapKitを使用して現在地を取得しながら、UISearchBarに入力した結果をtableViewに返すサンプルになります。
実際に使用する際は、現在地の取得の許可を得るために
info.plistに

Privacy - Location When In Use Usage Description
Privacy - Location Always and When In Use Usage Description

をかいてください。

現在地の取得が、

    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation])

常にアップデートされるため、

**メートル以内の変更は通知しないよう以下の関数を定義してあります。

func distinctUntilChangeGreaterThan(meters: CLLocationDistance) -> Observable<CLLocation> 

渡ってきたCLLocationを
.distinctUntilChangeGreaterThan(meters: CLLocationDistance(1000))
1000メートル以内の変化は通知しないように。


    var myLocationRelay = BehaviorRelay<CLLocation?>(value: nil)

        myLocationRelay
            .compactMap { $0 }
            .distinctUntilChangeGreaterThan(meters: CLLocationDistance(1000))
            .subscribe(onNext: { [weak self] coordinate in
                let span = MKCoordinateSpan(latitudeDelta: 0.001, longitudeDelta: 0.001)
                let region = MKCoordinateRegion(center: coordinate.coordinate, span: span)
                self?.searchCompleter.region = region
            })
            .disposed(by: bag)

すべてのコード

import CoreLocation
import MapKit
import RxCocoa
import RxSwift
import UIKit

class FindLocationViewController: UIViewController {
    var viewModel: FindLocationViewModel!
    var bag = DisposeBag()
    
    var searchCompleter = MKLocalSearchCompleter()
    var locationManager: CLLocationManager?
    var searchResults = [MKLocalSearchCompletion]()
    var myLocationRelay = BehaviorRelay<CLLocation?>(value: nil)
    
    @IBOutlet private weak var searchResultsTable: UITableView!
    @IBOutlet weak var searchBar: UISearchBar!
        
    override func viewDidLoad() {
        super.viewDidLoad()
        initialSettings()
        viewModel.setUp()
        setBind()
        whereIsMyLocation()
    }
    
    private func initialSettings() {
        searchBar.delegate = self
        searchResultsTable.delegate = self
        searchResultsTable.dataSource = self
        searchCompleter.delegate = self
        let categories: [MKPointOfInterestCategory] = [.fitnessCenter]
        let filters = MKPointOfInterestFilter(including: categories)
        searchCompleter.pointOfInterestFilter = .some(filters)
    }
    
    private func setBind() {
        myLocationRelay
            .compactMap { $0 }
            .distinctUntilChangeGreaterThan(meters: CLLocationDistance(1000))
            .subscribe(onNext: { [weak self] coordinate in
                let span = MKCoordinateSpan(latitudeDelta: 0.001, longitudeDelta: 0.001)
                let region = MKCoordinateRegion(center: coordinate.coordinate, span: span)
                self?.searchCompleter.region = region
            })
            .disposed(by: bag)
    }

    private func whereIsMyLocation() {
        locationManager = CLLocationManager()
        locationManager?.delegate = self
        locationManager?.requestWhenInUseAuthorization()

        if CLLocationManager.locationServicesEnabled() {
            locationManager!.startUpdatingLocation()
        }
    }
}

extension FindLocationViewController: CLLocationManagerDelegate {
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        guard let newLocation = locations.last else {
            return
        }

        let location: CLLocationCoordinate2D
            = CLLocationCoordinate2DMake(newLocation.coordinate.latitude, newLocation.coordinate.longitude)
        let formatter = DateFormatter()
        formatter.timeZone = TimeZone(identifier: "Asia/Tokyo")
        formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss'Z'"
        let date = formatter.string(from: newLocation.timestamp)
        
        print("緯度:", location.latitude, "経度:", location.longitude, "時間:", date)
        let ccLoctaion = CLLocation(latitude: location.latitude, longitude: location.longitude)

        myLocationRelay.accept(ccLoctaion)
    }
}

extension FindLocationViewController: UITableViewDelegate, UITableViewDataSource {
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)

        let result = searchResults[indexPath.row]
        let searchRequest = MKLocalSearch.Request(completion: result)

        let search = MKLocalSearch(request: searchRequest)
        search.start { response, _ in
            guard let coordinate = response?.mapItems[0].placemark.coordinate else {
                return
            }
            guard let name = response?.mapItems[0].name else {
                return
            }

            let lat = coordinate.latitude
            let lon = coordinate.longitude

            print(lat)
            print(lon)
            print(name)
            let preVC = self.presentingViewController as! WhichGymViewController
            let gymInfo = GymInfo(coodinate: coordinate, name: name)
            preVC.viewModel.selectedGymRelay.accept(gymInfo)
            self.dismiss(animated: true, completion: nil)
        }
    }
    
    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

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

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let searchResult = searchResults[indexPath.row]
        let cell = UITableViewCell(style: .subtitle, reuseIdentifier: nil)
        cell.textLabel?.text = searchResult.title
        cell.detailTextLabel?.text = searchResult.subtitle

        return cell
    }
}

extension FindLocationViewController: MKLocalSearchCompleterDelegate & UISearchBarDelegate {
    func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
        searchCompleter.queryFragment = searchText
    }

    func completerDidUpdateResults(_ completer: MKLocalSearchCompleter) {
        searchResults = completer.results
        searchResultsTable.reloadData()
    }

    func completer(_ completer: MKLocalSearchCompleter, didFailWithError error: Error) {
        // Error
    }
    
    func searchBarShouldBeginEditing(_ searchBar: UISearchBar) -> Bool {
        return true
    }
}

import CoreLocation
import Foundation
import RxSwift

extension CLLocationCoordinate2D: Equatable {}

public func == (lhs: CLLocationCoordinate2D, rhs: CLLocationCoordinate2D) -> Bool {
    return lhs.latitude == rhs.latitude && lhs.longitude == rhs.longitude
}

extension ObservableType where Element == CLLocation {
    func distinctUntilChangeGreaterThan(meters: CLLocationDistance) -> Observable<CLLocation> {
        return scan(CLLocation(), accumulator: { lastLocation, location in
            if lastLocation.distance(from: location) > meters {
                return location
            } else {
                return lastLocation
            }
        }).distinctUntilChanged { lhs, rhs in
            lhs.coordinate == rhs.coordinate
        }
    }
}
0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?