ジェスチャー

はじめにの例からわかるように、マップ ビューでは、ピンチやダブルタップでズームインなど、一般的なすべての地図ジェスチャがデフォルトでサポートされています。 次の表に、使用可能なジェスチャと、マップ上の対応するデフォルトの動作の概要を示します。

1 本の指で画面をタップします。 このジェスチャには、定義済みのマップアクションがありません。
地図を一定の大きさでズームするには、 1 本の指で画面を 2 回タップします。
1 本の指を押して画面に移動します。 このジェスチャには、定義済みのマップアクションがありません。
地図を移動 するには、 1 本の指を画面に合わせて押し続け、任意の方向に動かします。 指を持ち上げると、地図が少し勢いをつけて動き続けます。
地図を傾けるに は、 2 本の指を画面に合わせて押し続け、垂直方向に動かします。 他の方向の動作は事前に定義されていません。
一定の量をズームアウトするには、 2 本の指で画面をタップします。
連続的にズームインまたはズームアウトする には、 2 本の指を押したまま画面に移動し、その間の距離を増減します。

HERE SDK for iOS では、次のジェスチャがサポートされています。

  • タップ : TapDelegate
  • ダブルタップ : DoubleTapDelegate
  • 長押し : LongPressDelegate
  • パン : PanDelegate
  • 2 本指でパン : TwoFingerPanDelegate
  • 2 本指でタップ : TwoFingerTapDelegate
  • ピンチ回転 : PinchRotateDelegate

各デリゲートには、ユーザーが検出可能なアクション(特定のジェスチャの開始時や終了時など)を実行するたびに通知する専用のコールバックがあります。 通常は、ジェスチャが検出された後に、マップ マーカーを長押しした後に配置するなど、特定の動作をアプリケーションに追加します。

同じジェスチャには、一度に 1 人のデリゲートのみを設定できます。

地図操作を制御します

デリゲートリスナーを設定しても、ジェスチャのデフォルトのマップ動作には影響がありません。 個別に制御できます。 既定では、マップをダブルタップしたときのズームインなど、すべての既定の動作が有効になっています。

たとえば、次のように、ダブルタップ ( ズームイン ) および 2 本指タップ ( ズームアウト ) のデフォルトのマップジェスチャの動作を無効にできます。

mapView.gestures.disableDefaultAction(forGesture: .doubleTap)
mapView.gestures.disableDefaultAction(forGesture: .twoFingerTap)

既定のマップアクションを無効にしても、ジェスチャイベントをリッスンできます。 これは、例えば独自のズーム動作を実装するために、ジェスチャのデフォルトのアクションをオフにする場合に役立ちます。 タップと長押しを除くすべてのジェスチャで、デフォルトのマップアクションが提供されます。 詳細については、上の概要を参照してください。

デフォルトのマップジェスチャの動作を戻すには、次のように呼び出します。

mapView.gestures.enableDefaultAction(forGesture: .doubleTap)
mapView.gestures.enableDefaultAction(forGesture: .twoFingerTap)

ジェスチャー代理人を添付します

ジェスチャデリゲートをマップ ビューに添付する方法の例を見てみましょう。 デリゲートを設定するとすぐに、そのジェスチャに関連するすべてのイベントが専用のコールバック(onTap() TapDelegate の場合)経由で受信されます。 このプロトコルに準拠するクラスは、デリゲートとして機能します。

// Conform to the TapDelegate protocol.
func onTap(origin: Point2D) {
    let geoCoordinates = mapView.camera.viewToGeoCoordinates(viewCoordinates: origin)
    print("Tap at: \(String(describing: geoCoordinates))")
}

最後に、タップタッチイベントについてクラスに通知をもらい、リスニングを開始することをマップ ビューに伝えます。

mapView.gestures.tapDelegate = self

代理人を設定するとすぐに、ジェスチャが検出されたことを知らせる通知が届き始めます。

origin 、ジェスチャが発生したデバイスの画面からのポイントを指定します。 を呼び出すことで mapView.camera.viewToGeoCoordinates(viewCoordinates: origin)、ピクセルを地理座標に変換できます ( 上図を参照 ) 。

同様に、リスニングを停止するには、以下の電話番号を使用します。

mapView.gestures.tapDelegate = nil

連続したジェスチャ ( 長押し、ピンチ、パン、 2 本の指のパンなど ) の場合 begin 、ジェスチャの状態はジェスチャが検出されたことを示します。 指がディスプレイに触れている間に、指が持ち上げられたことを示すend状態になるまで、次のようなupdate状態になることがあります。

// Conform to the LongPressDelegate protocol.
func onLongPress(state: GestureState, origin: Point2D) {
    if (state == .begin) {
        let geoCoordinates = mapView.camera.viewToGeoCoordinates(viewCoordinates: origin)
        print("LongPress detected at: \(String(describing: geoCoordinates))")
    }

    if (state == .update) {
        let geoCoordinates = mapView.camera.viewToGeoCoordinates(viewCoordinates: origin)
        print("LongPress update at: \(String(describing: geoCoordinates))")
    }

    if (state == .end) {
        let geoCoordinates = mapView.camera.viewToGeoCoordinates(viewCoordinates: origin)
        print("LongPress finger lifted at: \(String(describing: geoCoordinates))")
    }
}

たとえば、長押しのイベントが検出された後も、ユーザーが指を画面に置いたままにしたり、移動したりすることがあります。 ただし begin 、長押しジェスチャが検出された時点でマークされるのはイベントのみです。

マップ マーカーを地図上に配置するには、長押しのジェスチャが便利です。 この例について は、 アプリの検索例を参照してください。

連続していないジェスチャ ( タップ、ダブルタップ、 2 本の指タップなど ) の GestureState 場合は、ジェスチャを処理する必要はありません。

チュートリアル - 地図のアクションをカスタマイズします

すでに確認したように、既定ではダブルタップジェスチャによって、都市レベルから通りのレベルに近い場所など、地図が個別のステップでズームされます。 このようなデフォルトのマップジェスチャアクションを無効にして独自のビヘイビアーを実装することも、既存のビヘイビアーに必要なアクションを追加することもできます。

必要に応じて、プラットフォームジェスチャ操作と HERE SDK ジェスチャ検出を組み合わせることもできます。 HERE SDK では、利便性のために、一般的な地図ジェスチャに重点を置いているため、すべての種類の低レベルのジェスチャイベントを提供するわけではありません。 より詳細な制御が必要な場合は、 HERE SDK で利用できるジェスチャ処理とネイティブのジェスチャ検出をいつでも組み合わせることができます。

このチュートリアルで は、カスタムズームアニメーションを有効にする方法を示します。 ユーザー がダブルタップまたは 2 本の指でタップした後、地図が徐々に拡大または縮小されます。

カスタムズーム動作を追加します

アニメーションから始めましょう。 このために、 Apple の CADisplayLink を使用し て、アニメーションをディスプレイの更新レートと同期させることができます。

便宜上 GestureMapAnimator、という名前の新しいクラスを作成します。このクラスは、ジェスチャに関連するすべてのアニメーションを処理する必要があります。 CameraLite 地図をカメラでズームする必要があるため、地図のインスタンスへの参照が必要です。

の内側 GestureMapAnimator には、ズームアニメーションへの参照があります。 CADisplayLink インスタンスの初期化に時間がかかることがあります。 対応するループメソッドも宣言します animatorLoopZoom ( 後で説明します ) 。

// A run loop to zoom in/out the map continuously until zoomVelocity is zero.
private lazy var displayLinkZoom = CADisplayLink(target: self,
                                                 selector: #selector(animatorLoopZoom))

これで stopAnimations() 、対応 CADisplayLink するインスタンスの進行中のすべてのアニメーションを一時停止するメソッドをすでに実装できました。

// Stop any ongoing zoom animation.
func stopAnimations() {
    displayLinkZoom.isPaused = true
}

既定では、地図は、途中のステップを使わずに、指が地図に触れる位置で、 1 つの個別のステップでズームイン / ズームアウトします。 このチュートリアルでは、既定のターゲットの場所でマップをズームする動作を単純化しますが、必要なアニメーションを紹介する中間ステップを追加します。 後で、指のタッチポイントでズームインする方法についても説明します。

必要なジェスチャイベントをフックしてみましょう。

mapView.gestures.disableDefaultAction(forGesture: .doubleTap)
mapView.gestures.disableDefaultAction(forGesture: .twoFingerTap)

// Conform to the DoubleTapDelegate protocol.
func onDoubleTap(origin: Point2D) {
    // Start our custom zoom in animation.
    gestureMapAnimator.zoomIn()
}

// Conform to the TwoFingerTapDelegate protocol.
func onTwoFingerTap(origin: Point2D) {
    // Start our custom zoom out animation.
    gestureMapAnimator.zoomOut()
}

魔法のような HERE はありません 2 つのジェスチャイベントをリッスンするだけです。 デフォルトのズーム動作を無効にするだけでかまいません。 上記の zoomIn() 方法および zoomOut() 方法を使用すると、以下に示す GestureMapAnimator 新しい方法を利用できます。

また startZoomVelocity 、ズームアニメーションの開始値と zoomDelta 現在 zoomVelocity の値が時間の経過とともに減少する量を決定するには、 2 つの定数を定義する必要があります。 startZoomAnimation() このメソッドは、実行ループの開始を処理します。

private let startZoomVelocity: Double = 0.1
private let zoomDelta: Double = 0.005

// Starts the zoom in animation.
func zoomIn() {
    isZoomIn = true
    startZoomAnimation()
}

// Starts the zoom out animation.
func zoomOut() {
    isZoomIn = false
    startZoomAnimation()
}

private func startZoomAnimation() {
    stopAnimations()

    zoomVelocity = startZoomVelocity
    displayLinkZoom.isPaused = false
    displayLinkZoom.add(to: .current, forMode: .common)
}

フラグを使用 isZoomIn すると、 1 つの方法でズームコードを処理できます。 アニメーションを開始する には、displayLinkZoomループを一時停止解除し、対応するセレクタメソッドanimatorLoopZoom()でズーム値を更新します。 このメソッドは、前に確認した stopAnimations() メソッドを呼び出して、 displayLinkZoom が再び一時停止するまで定期的に呼び出されます。

@objc private func animatorLoopZoom() {
    var zoom = camera.getZoomLevel()
    zoom = isZoomIn ? zoom + zoomVelocity : zoom - zoomVelocity;
    camera.setZoomLevel(zoom)
    zoomVelocity = zoomVelocity - zoomDelta
    if (zoomVelocity <= 0) {
        stopAnimations()
    }
}

上記の単純なアルゴリズムは zoomVelocity 、 0 に近い 0.1 の値を補間します。 zoomVelocity は、マップを徐々にズームするために使用できるアニメーション化された値です。 開始値は startZoomVelocity 0.1 で定義されています。 zoomVelocity 現在のズーム値を前のフレームから変更するには、 as 引数を使用します。 zoomVelocity ゆっくりと 0 に達すると、ズームステップのサイズが小さくなります。

ブール型フラグ isZoomIn は、地図を拡大または縮小する必要があるかどうかを示します。 これにより、両方のズームケースでコードを使用できます。 唯一の違い は、アニメーション化された値zoomVelocityが現在のズーム レベルから追加または削除されることです。

タッチポイントでズームインします

上の図では、タッチの原点を無視してカスタムの拡大動作を単純化しています。 マップは、指が画面に触れた位置でズームインするのが理想的です。 これを可能にする方法 既定では、すべてのプログラミングマップ操作は、マップ ビューを中心とするターゲットのアンカーポイントに基づいています。 そのため、地図は地図の中央の位置をズームします。 ターゲットの基準点の概念 HERE の詳細については、を参照してください。

このポイントを、指が画面に触れたポイントに変更するだけでかまいません。 その結果、マップをズームする呼び出しでは、この新しいアンカーポイントがターゲットとして使用されます。 ダブルタップジェスチャは Point2D 、タッチの起点を示すインスタンスをすでに提供しています。 ターゲットのアンカーポイントの変更は簡単です。ズームイン方法には、コードの行をさらに 1 行追加するだけで済みます。

func zoomIn(_ mapView: MapViewLite, _ origin: Point2D) {
    // Change the anchor point to zoom in at the touched point.
    camera.targetAnchorPoint = getAnchorPoint(mapView, origin)

    isZoomIn = true
    startZoomAnimation()
}

アンカーポイントは 、正規化 された座標で指定できます。 (0, 0) は、マップ ビューの左上隅と右下隅 (1,1) を示します。 反対に、ダブルタップジェスチャから送られたタッチポイントがマップ ビューのピクセル位置を提供しています。 これを行うには、次の方法で新しいアンカーを計算する必要があります。

private func getAnchorPoint(_ mapView: MapViewLite, _ origin: Point2D) -> Anchor2D {
    let scaleFactor = UIScreen.main.scale
    let mapViewWidthInPixels = Double(mapView.bounds.width * scaleFactor)
    let mapViewHeightInPixels = Double(mapView.bounds.height * scaleFactor)

    let normalizedX = (1.0 / mapViewWidthInPixels) * origin.x
    let normalizedY = (1.0 / mapViewHeightInPixels) * origin.y

    let transformCenter = Anchor2D(horizontal: normalizedX,
                                   vertical: normalizedY)
    return transformCenter
}

まず、デバイスの種類に応じてマップ ビューのピクセルサイズを取得する必要 scaleFactorがあります。 次に、そのビューで正規化されたタッチポイントを計算して、新しい Anchor2D インスタンスを構成します。 この新しいターゲットアンカーポイントを設定すると、マップがタッチした位置でスムーズにズームインされます。

ズームアウトは 2 本の指で行うため、地図の中央をターゲットとして使用してジェスチャを実行することをお勧めします。 そのため、アニメーションを停止するとき 、必要に応じてアンカーポイントをデフォルトの位置にリセットすることができます。

// Reset anchor point to its default location.
camera.targetAnchorPoint = Anchor2D(horizontal: 0.5, vertical: 0.5)

アプリケーションで別のアンカーポイントが必要な場合は、ズームアニメーションを開始する前に前のアンカーポイントを保存し、アニメーションが停止した後で元のアンカーポイントにリセットできます。 ターゲットのアンカーポイントによって、マップ ビューを基準にして新しい地図の場所が表示される場所も決まります。通常は、この場所がビューの中央にある必要があります。

これで、小旅行になりました。 上記のコードスニペットは、ご自身のニーズに合わせて自由に変更できます。 たとえば、さまざまな補間法、さまざまなアニメーション値、またはアニメーションのデュレーションを使用して再生を開始します。

」に一致する結果は 件です

    」に一致する結果はありません