1
0

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.

検索した場所にピンを刺す

Last updated at Posted at 2021-05-29

ゴール

以前、画面に地図を表示し、ピンを決め打ちで指すことができるようになった。このピンを、UIから取得した任意の場所に刺せるようにしたい。

調査

  • 普通にstoryboardでボタン、ラベルを配置すると、地図がそれらより全面に描画される?ため、検索欄などを儲けることができない。
  • 地点検索の実装に関して、公式にめちゃくちゃご親切に書いてあるけど、実行して見ると動かない部分もあり、理解が追いつかないので一つずつ進める。
  • 地図より前面にボタンなどを配置しようと思うと、storyboardではなく、ViewController.swiftのPG上で実装する必要がありそう。実行の順番の問題?
  • delegateについて気になったので調査。ほぼinterefaceじゃんと思いましたが、厳密には違うみたい。全然違った。delegate
  • 地名や建物名から検索を行う機能はいくつかあるみたいだが、この3つ(ジオコーディング、オートコンプリートサービス、プレイス検索)の中だと、曖昧検索の結果の中から1つを選択することのできる、「オートコンプリートサービス」が良い。

実装

ボタンを配置する。

func makeButton() {
    let btnLaunchAc = UIButton(frame: CGRect(x: 25, y: 25, width: 80, height: 80))
    btnLaunchAc.backgroundColor = .clear
    btnLaunchAc.setTitle("Pos", for: .normal)
    btnLaunchAc.setTitleColor(.blue, for: .normal)
    btnLaunchAc.addTarget(self, action: #selector(getMyPosition), for: .touchUpInside)
    self.view.addSubview(btnLaunchAc)
}

ラベル×2を配置する。

func makeLabel() {
    nameLabel = UILabel(frame: CGRect(x:105, y:25, width:250, height:40))
    addressLabel = UILabel(frame: CGRect(x:105, y:65, width:250, height:40))
    nameLabel.textColor = .blue
    addressLabel.textColor = .blue
    //nameLabel.backgroundColor = .white
    //addressLabel.backgroundColor = .white
    nameLabel.text = "name"
    addressLabel.text = "address"
    self.view.addSubview(nameLabel)
    self.view.addSubview(addressLabel)
}

テキストフィールドを配置

func makeTextField() {
    textField = UITextField()
    textField.frame = CGRect(x: 10, y: 100, width: UIScreen.main.bounds.size.width-20, height: 38)
    textField.placeholder = "目的地"
    textField.keyboardType = .default
    textField.borderStyle = .roundedRect
    textField.returnKeyType = .done
    textField.clearButtonMode = .always
    self.view.addSubview(textField)
}

完了ボタンを押すとキーボードが閉じるように以下3点を追加(参考

//1
class ViewController: UIViewController, CLLocationManagerDelegate, UITextFieldDelegate  {

//2
textField.delegate = self

//3
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
    self.view.endEditing(true)
    return false
}

こちらを参考に、テキストフィールドの値が変わるたびに呼ばれる関数を定義(オートコンプリートを呼びたい)

// textFieldに対して追加
textField.addTarget(self, action: #selector(self.textFieldDidChange(_:)), for: UIControl.Event.editingChanged)

// 関数を追加
@objc func textFieldDidChange(_ textFiled: UITextField) {

}

→オートコンプリートを勘違いしてた。文字が変更されるたびに呼ぶ必要はなく、入力が開始されるタイミングで一度呼べば良いので、実装を以下に変更。(参考

// textFieldに対して追加
textField.addTarget(self, action: #selector(self.textFieldChange(_:)), for: UIControl.Event.editingDidBegin)

// 関数を追加
@objc func textFieldChange(_ textFiled: UITextField) {
}

テキストフィールドが呼ばれたタイミングで、別画面が表示されるよう、textFieldChange関数内に以下を追加する。

@objc func textFieldChange(_ textFiled: UITextField) {
    // ViewController?を追加
    let autocompleteController = GMSAutocompleteViewController()

    // delegateを指定する
    autocompleteController.delegate = self

    // autocompleteControllerを表示する
    present(autocompleteController, animated: true, completion: nil)
}

autocompleteControllerで行われる処理を実装する。

extension ViewController: GMSAutocompleteViewControllerDelegate {
    // ①
    func viewController(_ viewController: GMSAutocompleteViewController, didAutocompleteWith place: GMSPlace) {
        textField.text = place.name
        dismiss(animated: true, completion: nil)
    }

    // ②
    func viewController(_ viewController: GMSAutocompleteViewController, didFailAutocompleteWithError error: Error) {
        // TODO
    }

    // ③
    func wasCancelled(_ viewController: GMSAutocompleteViewController) {
        dismiss(animated: true, completion: nil)
    }
}

GMSAutocompleteViewControllerDelegateに飛んで、各メソッドの役割を確認してみる。

利用可能なオートコンプリート予測から場所が選択されたときに呼び出されます。
このメソッドの実装では、View Controller が終了しないため、View Controller を終了する必要があります。
自分自身をdismissしてください。

オートコンプリートの予測または場所を取得するときに再試行不可能なエラーが発生したときに呼び出されます
詳細。 再試行不可能なエラーは、すぐに修正される可能性が低いエラーとして定義されます
操作を再試行してください。

ユーザーが |GMSAutocompleteViewController| の [キャンセル] ボタンをタップしたときに呼び出されます。
このメソッドの実装では、View Controller が終了しないため、View Controller を終了する必要があります。
自分自身をdismissしてください。

エラー処理は一旦置いておいて、予測された場所の一覧の中から一つを選んだときに、テキストフィールドに値が入るように実装した。

スクリーンショット 2021-05-29 11.18.18.png

スクリーンショット 2021-05-29 11.18.50.png

あとは、選択した地名やアドレスから、緯度軽度を取得して、ピンを指す値として渡してあげる。
マーカーと、カメラの位置を指定する部分を関数化して、初めに呼ばれるとき以外は、指定したカメラのポジションに移動するように実装

func PointPlace(pos: CLLocationCoordinate2D, title: String?) {
    camera = GMSCameraPosition.camera(withTarget: pos, zoom: 10.0)
    if(first){
        mapView = GMSMapView.map(withFrame: self.view.frame, camera: camera)
        self.view.addSubview(mapView)
    }else{
        self.mapView.animate(to: camera)
    }

    marker.position = pos
    marker.title = title
    marker.map = mapView
}

上記①の中で、位置と名前を渡してあげる。

func viewController(_ viewController: GMSAutocompleteViewController, didAutocompleteWith place: GMSPlace) {
    textField.text = place.name
    // 位置と名前を渡す
    PointPlace(pos: place.coordinate, title: place.name)
    dismiss(animated: true, completion: nil)
}

できた!

スクリーンショット 2021-05-29 17.43.37.png
スクリーンショット 2021-05-29 17.43.55.png
スクリーンショット 2021-05-29 17.44.11.png

1
0
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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?