LoginSignup
1
1

More than 3 years have passed since last update.

Swift MkMapViewで地図アプリ作成してみた(21)- 周辺検索した地点をセミモーダルビューに表示する

Last updated at Posted at 2020-03-29

記事一覧

Swift MkMapViewで地図アプリ作成してみた(記事一覧)

これを作ります

googleMapみたいなモーダルビューを作成します。
modal.png CustomFloatingPanelLayout2.png CustomFloatingPanelLayout3.png

セミモーダルビューを表示する

FloatingPanelをインストールする

swift単体では実装できません。
簡単な方法を探した結果、CocoaPodsで'FloatingPanel'をインストールすることにしました。
ターミナルから下記を実行してインストールする。

pod 'FloatingPanel'

モーダルビューのデザインパターンを設定する

FloatingPanelLayoutを継承したクラスを作成し、モーダルビューのデザインパターンを設定する。
insetForで、フリックした時のモーダルビューの表示領域を設定する。

CustomFloatingPanelLayout.swift:デザインパターンを設定する
import Foundation
import FloatingPanel

class CustomFloatingPanelLayout: FloatingPanelLayout {
    // セミモーダルビューの初期位置
    var initialPosition: FloatingPanelPosition {
        return .half
    }

    var topInteractionBuffer: CGFloat {
        return 0.0
    }

    var bottomInteractionBuffer: CGFloat {
        return 0.0
    }

    // セミモーダルビューの各表示パターンの高さを決定するためのInset
    func insetFor(position: FloatingPanelPosition) -> CGFloat? {
        var ret: CGFloat!
        switch position {
        case .full:
            ret = nil//56.0
        case .half:
            ret = 262.0
        case .tip:
            ret = 75.0
        default:
            ret = nil
        }
        return ret
    }

    // セミモーダルビューの背景Viewの透明度
    func backdropAlphaFor(position: FloatingPanelPosition) -> CGFloat {
        return 0.0
    }
}

セミモーダルビューを表示する

WalkViewControllerビューから、PointPopupViewControllerビューをセミモーダルで表示する。

WalkViewController.swift:セミモーダルビューを表示する
import FloatingPanel

class WalkViewController:   UIViewController, 割愛
{
    var floatingPanelController: FloatingPanelController!

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.

        // モーダルビュー
        floatingPanelController = FloatingPanelController()
        floatingPanelController.delegate = self
    }

    // PointPopupViewControllerビューをセミモーダルで表示する
    func showPointPopupView() {
        // かどを丸くする
        floatingPanelController.surfaceView.cornerRadius = 24.0
        // PointPopupViewControllerをセミモーダルに登録する
        let viewCnt = PointPopupViewController()
        floatingPanelController.set(contentViewController: viewCnt)
        // セミモーダルビューを表示する
        floatingPanelController.addPanel(toParent: self, belowView: nil, animated: true)
        floatingPanelController.move(to: .half, animated: true)
    }
}

FloatingPanelControllerDelegateを実装して、カスタマイズしたデザインパターンを有効にする。
また、セミモーダルに登録したビューのサイズ変更を有効にする。
これを実装しないと、セミモーダルビューは表示されない。

WalkViewController.swift:FloatingPanelControllerDelegateを実装
extension WalkViewController : FloatingPanelControllerDelegate {
    // カスタマイズしたデザインパターンを返す
    func floatingPanel(_ vc: FloatingPanelController, layoutFor newCollection: UITraitCollection) -> FloatingPanelLayout? {
        return CustomFloatingPanelLayout()
    }

    // セミモーダルビューをサイズ変更した時に、登録したビューのサイズを変更する
    func floatingPanelDidEndDragging(_ vc: FloatingPanelController, withVelocity velocity: CGPoint, targetPosition: FloatingPanelPosition) {

        let appDelegate: AppDelegate = UIApplication.shared.delegate as! AppDelegate
        appDelegate.pointPopupViewController.resize(targetPosition)
    }
}

セミモーダルビューを消去する

消去する場合は、以下の通り。

WalkViewController.swift:セミモーダルビューを消去する
    // PopUp画面の消去
    func ExitPointPopupView() {
        // セミモーダルビューを消去する
        floatingPanelController.removePanelFromParent(animated: true)
    }

周辺検索した地点をセミモーダルビューに表示する

検索バーに入力したワードを地図検索して、アノテーションに表示する

検索バーを表示する。

WalkViewController.swift:検索バーを表示する
class WalkViewController:   UIViewController,
                            UISearchBarDelegate, 割愛
{
    @IBOutlet var searchBar: UISearchBar!

    override func viewDidLoad() {

        // 検索バーの位置を指定して表示
        searchBar.frame = CGRect(x: 0, y: height-50, width: width, height: 50)
        self.view.addSubview(searchBar)

    }

検索結果を表示するためのアノテーションを準備する。

WalkViewController.swift:アノテーションのクラスを定義し、リスト変数を宣言する。

// MKPointAnnotation拡張用クラス
class MapAnnotationWalk: MKPointAnnotation {
    var pinColor: UIColor = .red

    func setPinColor(_ color: UIColor) {
        pinColor = color
    }
}

class WalkViewController:   UIViewController,
                            UISearchBarDelegate, 割愛
{
    // 検索結果を保存するアノテーションのリスト
    var annotationList = [MapAnnotationWalk]()

検索バーに入力された文字を地図検索して、アノテーションリストに設定して表示する。

WalkViewController.swift:検索地点をアノテーションリストに表示
    //検索バーで検索ボタン押した時に呼び出されるメソッド
    func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
        // キーボードを消去する
        searchBar.resignFirstResponder()

        // 未入力なら無視する
        if "" == searchBar.text {
            return
        }

        // 検索条件を作成する。
        let request = MKLocalSearch.Request()
        request.naturalLanguageQuery = searchBar.text

        // 検索範囲はマップビューと同じにする。
        request.region = mapView.region

        // ローカル検索を実行する。
        let localSearch:MKLocalSearch = MKLocalSearch(request: request)
        localSearch.start(completionHandler: {(result, error) in
            if nil != result {
                for placemark in (result?.mapItems)! {
                    if(error == nil) {
                        //検索された場所にピンを刺す。
                        let annotation = MapAnnotationWalk()
                        annotation.coordinate =     CLLocationCoordinate2DMake(placemark.placemark.coordinate.latitude, placemark.placemark.coordinate.longitude)
                        annotation.title = placemark.placemark.name
                        annotation.subtitle = "〒\(placemark.placemark.postalCode ?? "")\n\(placemark.placemark.administrativeArea ?? "")\(placemark.placemark.locality ?? "")\n\(placemark.placemark.name ?? "")" //placemark.placemark.title
                        annotation.setPinColor(.green)
                        self.annotationList.append(annotation)
                        self.mapView.addAnnotation(annotation)
                    }
                    else {
                        //エラー
                        print(error.debugDescription)
                    }
                }
            }
        })
    }

ローカル検索で取得できる情報は、こちらを参照。
実行結果は以下の通り。
image1.png

選択したアノテーションをセミモーダルビューに表示する

セミモーダルビューに表示する情報を格納する変数を宣言しておく。

WalkViewController.swift:セミモーダルビューに表示する情報
    // タップした地点の情報
    var tapPointTitle: String! = ""                 // タップした地点のタイトル
    var tapStreetAddr: String! = ""                 // 住所
    var tapDistance: CLLocationDistance! = 0        // 距離

アノテーションを選択した時に呼ばれるデリゲートを実装する。
先ほど宣言した変数に情報を設定してから、セミモーダルビューを表示する。

WalkViewController.swift:
    // アノテーションを選択した際に呼ばれるデリゲート
    func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
        let annotation = view.annotation

        // POP UP画面に表示する住所
        tapPointTitle = annotation?.title ?? ""
        tapStreetAddr = annotation?.subtitle ?? ""

        // POP UP画面に表示する距離
        let coordinate = CLLocationCoordinate2D(latitude: (annotation?.coordinate.latitude)!, longitude: (annotation?.coordinate.longitude)!)
        tapDistance = calcDistance(mapView.userLocation.coordinate, coordinate)

        // POP UP画面で探索する地点
        tapRoutePoint = coordinate

        // セミモーダルビューの表示
        showPointPopupView()
    }

セミモーダルビューに表示する情報を取得するためのメソッドを準備しておく。

WalkViewController.swift:セミモーダルビューから呼ばれるメソッド
    // タップした地点の名称を取得する
    public func getTapPointTitle() -> String {
        return tapPointTitle
    }

    // ロングタップした住所を取得する
    public func getTapStreetAddr() -> String {
        return tapStreetAddr
    }

    // ロングタップした位置までの距離を取得する
    public func getTapDistance() -> String {
        var retVal: String!
        let dist = Int(tapDistance)
        if (1000 > dist) {
            retVal = dist.description + " m"
        }
        else {
            let dDist: Double = floor((Double(dist)/1000)*100)/100
            retVal = dDist.description + " km"
        }

        return retVal
    }

セミモーダルに登録したPointPopupViewControllerビューの実装

選択したアノテーションの情報を表示する

PointPopupViewController.swift:セミモードに登録したビューの実装
import UIKit
import FloatingPanel

class PointPopupViewController: UIViewController
{
    var lblDistance: UILabel!
    var lblTitle: UILabel!
    var lblStreetAddr: UILabel!

    override func viewDidLoad() {
        super.viewDidLoad()

        lblTitle.text = appDelegate.walkViewController.getTapPointTitle().description
        lblDistance.text = appDelegate.walkViewController.getTapDistance().description
        lblStreetAddr.text = "住所 \n" + appDelegate.walkViewController.getTapStreetAddr().description
    }

表示した結果は以下の通り。
セミモーダルビューは以下の通りhalfので表示したので、表示結果はデザインパターンのhalfに従って以下の画像の通りとなる。

floatingPanelController.move(to: .half, animated: true)

image2.png

※画像は英語ですが、デバイスの言語設定が日本語なら日本語が表示されます。

フリックすれば、表示はfull、tipと切り替わる。

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