#記事一覧
Swift MkMapViewで地図アプリ作成してみた(記事一覧)
#これを作ります
googleMapみたいなモーダルビューを作成します。
#セミモーダルビューを表示する
###FloatingPanelをインストールする
swift単体では実装できません。
簡単な方法を探した結果、CocoaPodsで'FloatingPanel'をインストールすることにしました。
ターミナルから下記を実行してインストールする。
pod 'FloatingPanel'
###モーダルビューのデザインパターンを設定する
FloatingPanelLayoutを継承したクラスを作成し、モーダルビューのデザインパターンを設定する。
insetForで、フリックした時のモーダルビューの表示領域を設定する。
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ビューをセミモーダルで表示する。
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を実装して、カスタマイズしたデザインパターンを有効にする。
また、セミモーダルに登録したビューのサイズ変更を有効にする。
これを実装しないと、セミモーダルビューは表示されない。
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)
}
}
###セミモーダルビューを消去する
消去する場合は、以下の通り。
// PopUp画面の消去
func ExitPointPopupView() {
// セミモーダルビューを消去する
floatingPanelController.removePanelFromParent(animated: true)
}
#周辺検索した地点をセミモーダルビューに表示する
###検索バーに入力したワードを地図検索して、アノテーションに表示する
検索バーを表示する。
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)
}
検索結果を表示するためのアノテーションを準備する。
// MKPointAnnotation拡張用クラス
class MapAnnotationWalk: MKPointAnnotation {
var pinColor: UIColor = .red
func setPinColor(_ color: UIColor) {
pinColor = color
}
}
class WalkViewController: UIViewController,
UISearchBarDelegate, 割愛
{
// 検索結果を保存するアノテーションのリスト
var annotationList = [MapAnnotationWalk]()
検索バーに入力された文字を地図検索して、アノテーションリストに設定して表示する。
//検索バーで検索ボタン押した時に呼び出されるメソッド
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)
}
}
}
})
}
ローカル検索で取得できる情報は、こちらを参照。
実行結果は以下の通り。
###選択したアノテーションをセミモーダルビューに表示する
セミモーダルビューに表示する情報を格納する変数を宣言しておく。
// タップした地点の情報
var tapPointTitle: String! = "" // タップした地点のタイトル
var tapStreetAddr: String! = "" // 住所
var tapDistance: CLLocationDistance! = 0 // 距離
アノテーションを選択した時に呼ばれるデリゲートを実装する。
先ほど宣言した変数に情報を設定してから、セミモーダルビューを表示する。
// アノテーションを選択した際に呼ばれるデリゲート
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()
}
セミモーダルビューに表示する情報を取得するためのメソッドを準備しておく。
// タップした地点の名称を取得する
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ビューの実装
###選択したアノテーションの情報を表示する
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)
※画像は英語ですが、デバイスの言語設定が日本語なら日本語が表示されます。
フリックすれば、表示はfull、tipと切り替わる。