LoginSignup
19
17

More than 5 years have passed since last update.

iOSのMKMapViewで、GoogleMap的なダブルタップ → 上下スライド でのズームイン・アウトとダブルタップでのズームを共存させる

Last updated at Posted at 2016-05-09

始めに

iOS標準の地図を提供するMapKitのMKMapViewで、
GoogleMapのようなダブルタップからの上下スライドでズームイン・アウトする機能を実装した。
単にズームイン・アウトをするだけなら単純だったのだが、
デフォルトで設定してあるダブルタップでのズームの機能を残すのに少し苦労した。

コード

ViewController.swift

import MapKit

class ViewController: UIViewController {

    /* ドラッグの位置記憶用の変数 */
    var dragPoint: CGPoint?

    var mapView: MKMapView!
    override func viewDidLoad() {
        super.viewDidLoad()
        mapView = MKMapView(frame: view.bounds)
        view.addSubview(mapView)

        let doubleLongPress = UILongPressGestureRecognizer(target: self, action: #selector(ViewController.doubleLongPress(_:)))

        /* ダブルタップ後、即座にLongPress状態に移るように */
        doubleLongPress.minimumPressDuration = 0
        doubleLongPress.numberOfTapsRequired = 1

        mapView.addGestureRecognizer(doubleLongPress)

        doubleLongPress.delegate = self

        /* MKMapViewの機能が実装してあるSubViewを引っ張ってきて、設定してあるDoubleTapGestureRecognizerにdelegateを設定する */
        mapView.subviews[0].gestureRecognizers?.forEach({ (element) in
            if let recognizer = (element as? UITapGestureRecognizer) where recognizer.numberOfTapsRequired == 2 {
                element.delegate = self
            }
        })
    }
}

extension ViewController: UIGestureRecognizerDelegate {
    /* このzoomメソッドの実装は適当 */
    func zoom(magnification: Double) {
        var region = mapView.region
        let span = region.span
        region.span = MKCoordinateSpan(latitudeDelta: span.latitudeDelta * magnification, longitudeDelta: span.longitudeDelta * magnification)
        mapView.setRegion(region, animated: false)
    }

    /* ダブルタップ → 上下動  で、ズームイン / アウト する (GoogleMap的な挙動) */
    func doubleLongPress(recognizer: UILongPressGestureRecognizer) {
        let state = recognizer.state
        let location = recognizer.locationInView(recognizer.view)
        switch state {
        case .Began:
            dragPoint = location
        case .Changed:
            /* 上に動いたか下に動いたか判断 */
            let diffY = Double(location.y - dragPoint!.y)
            let magnification = 1 - diffY * 0.01
            self.zoom(magnification)
            dragPoint = location
        default:
            break
        }
    }

    /* MKMapViewに元から設定されているDoubleTapと、自分で設定したLongPressを同時に機能させる */
    func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        return true
    }
}

forced unwrapとかエラー処理とか設定する値のvalidationとかには適宜対応する。

ポイント

  • 元から設定してあるDoubleTapのrecognizerを無理矢理引っ張ってくる

  • gestureRecognizer(gestureRecognizer: UIGestureRecognizer,
    shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer)

    のdelegateメソッドを実装して、
    元から実装してあるDoubleTapと自分で設定したLongPressを同時に機能させる

これで、ダブルタップ → 上下スライド でのズームイン・アウトと単なるダブルタップでのズームが共存できるはず。
上下スライドを行った後で勝手にズームしないか心配だったが、その辺は都合よく動いた。

終わりに

iOS標準の地図にダブルタップ → 上下スライド でズームイン・アウトの機能が実装されていないのは特許の問題だろうか。
だとしたら勝手に実装したらまずい気もする。探しても実装した例が見つからなかったのはそのせいだろうか。とか思った。

19
17
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
19
17