MKMapViewのCalloutからPopOverで詳細画面を表示する | Swift2

開発環境:xcode7、iOS9.2
言語:swift2.1

対象読者:初心者

前置き

今回はドハマリしたApple標準のマップ「MapView」にピンをぶっさして、それに吹き出しを表示、そしてポップオーバーの画面に遷移するところまでを投稿したいと思います

ポップオーバーとはいわゆる大きい吹き出しでこんなところに使われています

  • Apple標準のマップアプリの東京駅をタップした時、詳細画面に移動した時




  • 同じく標準のApp Storeアプリのウィッシュリスト




  • SmartNewsアプリの記事を長押しした時の表示



吹き出しを表示するところまでは順調に行ったんですが、2016年1月時点ではポップオーバのサンプルコードがリファレンスに書いておらず、Swift開発弱者の自分は八方ふさがりでした

ちなみにAppleのリファレンス
UIPopoverPresentationController(外部リンク)
iOS8.0からUIPopoverControllerが廃止?されUIPopoverPresentationControllerが推奨されています
この新しいUIPopoverPresentationControllerから今までできなかったiPhoneでのポップオーバ表示ができるようになったらしいですよ

本題

とりあえず画面構成
ピンは本初子午線と赤道の交差点に立ててます。座標で言うと(0,0)


ピンをタップ


「i」をタップして画面遷移
適当にボタンとかを入れたので、特に動きません
動くところはTextViewがスクロールするところくらい


ストーリーボード

最初に表示されるのが左側です。MapViewを全面に表示させています

右側が画面遷移をした状態(以後DetailContorollerという)
前述のとおり適当にぶっこんでいます

DetailControllerにはStoryBoardIDで名前をつけています
DetailControllerをクリックし、右側の「Show the Identity inspector」タブをクリック(下の画像で言う、右上に6つ並んでるやつです)
IdentityのStoryBoardIDのところに「locationDetail」 と入力しました

あと、詳細情報を管理する新しいクラスファイルを作って、ClassのところにDetailControllerと記述しました
レイアウトは適当に頑張ってください

ソースコード
こっちが、ViewController
始めっからある方です

import UIKit
import MapKit

class ViewController: UIViewController,MKMapViewDelegate,UIPopoverControllerDelegate {
    
    @IBOutlet weak var mapView: MKMapView!
    override func viewDidLoad() {
        super.viewDidLoad()

        mapView.delegate=self
        
        let location:CLLocationCoordinate2D = CLLocationCoordinate2DMake(0, 0)
        
        //ピンを生成
        let pinAnnotation = MKPointAnnotation()
        //ピンを置く場所を設定
        pinAnnotation.coordinate  = location
        //ピンのタイトルの設定
        pinAnnotation.title       = "test"
        //ピンのサブタイトルを設定
        pinAnnotation.subtitle    = "test subtitle"
        
        //ピンを地図上に追加
        mapView.addAnnotation(pinAnnotation)
        
        
        let myLatDist: CLLocationDistance = 100
        let myLonDist: CLLocationDistance = 100
        
        // Regionを作成.
        let myRegion: MKCoordinateRegion = MKCoordinateRegionMakeWithDistance(location, myLatDist, myLonDist);
        
        // MapViewに反映.
        mapView.setRegion(myRegion, animated: true)
    }
    
    
    func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
        if annotation is MKUserLocation {
            return nil
        }
        
        // Identifier
        let myAnnotationIdentifier = "myAnnotation"
        
        // AnnotationViewをdequeue
        var myAnnotationView : MKAnnotationView! = mapView.dequeueReusableAnnotationViewWithIdentifier(myAnnotationIdentifier)
        
        //アノテーションの右側につけるボタンの宣言
        let button:UIButton = UIButton(type: UIButtonType.InfoLight)

        if myAnnotationView == nil {
            myAnnotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: myAnnotationIdentifier)
            //アノテーションの右側にボタンを付ける
            myAnnotationView.rightCalloutAccessoryView = button
            myAnnotationView.canShowCallout = true  
        }
        return myAnnotationView          
    }
    
    //ボタンを押された時
    func mapView(mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
        
        //アノテーションを消す
        if let annotation = view.annotation {
            mapView.deselectAnnotation(annotation, animated: true)
        }
        
        //ストーリーボードの名前を指定(何も変更ない状態であれば「Main」だと思う)
        let storyboard = UIStoryboard(name: "Main", bundle: nil)
        //viewにつけた名前を指定
        let vc = storyboard.instantiateViewControllerWithIdentifier("locationDetail")
        //popoverを指定する
        vc.modalPresentationStyle = UIModalPresentationStyle.Popover
        
        presentViewController(vc, animated: true, completion: nil)
        
        let popoverPresentationController = vc.popoverPresentationController
        popoverPresentationController?.sourceView = view
        popoverPresentationController?.sourceRect = view.bounds
        
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}
なんとなく解説すると、calloutAccessoryControlTappedのところ( ボタンを押されあた時)で、コールアウトが押された時の処理を書きます
今回は、最初のタップで表示されたコールアウトを非表示にして、画面遷移をしています
viewに付けた名前を指定のところで、さっき入力した「StoryBoardID」を入力することで、コードを使わなくても画面を構成することが可能です

特に意味ないけど、DetailControllerも
いつもどおりに「control+ドラッグ」でボタンなどのコードを書くことが出来ます
import UIKit

class DetailController: UIViewController {

       override func viewDidLoad() {
        super.viewDidLoad()
        

    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

まとめ

けっこうめんどくさい、サンプルコードなさすぎ
いや、自分の検索の仕方が下手くそなのかな?

コメント