地図を操作する
デフォルトでは、HERE SDKマップビューは、ピンチやダブルタップによるズームインなど、一般的なすべてのマップジェスチャーをサポートしています。以下の表に、使用できるジェスチャーとそれに対応する地図のデフォルト動作をまとめました。
![]() |
1 本の指で画面をタップします。このジェスチャーには事前定義されたマップアクションはありません。 |
![]() |
地図を一定の倍率でズーム インするには、1 本の指で画面を 2 回タップします。 |
![]() |
1本の指で画面をプレスアンドホールドします。このジェスチャーには事前定義されたマップアクションはありません。 |
![]() |
地図を移動させるには、画面を 1 本の指でプレス アンド ホールドしたまま、任意の方向に動かします。指を離した後も、地図は勢いで少し動き続けます。 |
![]() |
地図を傾斜させるには、2 本の指を画面にプレス アンド ホールドしたまま垂直に動かします。他の方向への動作は事前に定義されていません。 |
![]() |
一定の倍率でズーム アウトするには、2 本の指で画面をタップします。 |
![]() |
連続してズーム インまたはズーム アウトするには、2 本の指で画面をプレス アンド ホールドしたまま、指の間の距離を増減します。 地図を連続的に回転させるには、2 本の指を画面にプレス アンド ホールドして、両方の指を回転させるか一方の指を動かして、指の間の角度を変更します。 |
HERE SDK for iOS では次のジェスチャーがサポートされています。
- タップ:
TapDelegate - ダブル タップ:
DoubleTapDelegate - 長押し:
LongPressDelegate - パン:
PanDelegate - 2 本指のパン:
TwoFingerPanDelegate - 2 本指のタップ:
TwoFingerTapDelegate - ピンチによる回転:
PinchRotateDelegate
各デリゲートには、検知される可能性のあるアクションをユーザーが実行するたびに通知する専用のコールバックがあります。たとえば、特定のジェスチャーの開始や終了などです。通常、ジェスチャーが検知された後、アプリケーションに特定の動作を追加する必要があります。たとえば、長押しした後にマップ マーカーを配置するなどです。
同じジェスチャーに一度に設定できるデリゲートは 1 つのみです。
ジェスチャー デリゲートを実装する
ジェスチャー デリゲートをマップ ビューに実装する方法の例を見てみましょう。デリゲートを設定するとすぐに、専用のコールバック (TapDelegate の場合は onTap()) から、そのジェスチャーに関連するすべてのイベントを受信します。このプロトコルに準拠するクラスがデリゲートとして次のように機能します。
// Conform to the TapDelegate protocol.
func onTap(origin: Point2D) {
let geoCoordinates = mapView.viewToGeoCoordinates(viewCoordinates: origin)
print("Tap at: \(String(describing: geoCoordinates))")
}
最後に、マップ ビューにクラスがタップ タッチ イベントに関する通知を取得することを伝え、リッスンを開始します。
mapView.gestures.tapDelegate = self
デリゲートを設定するとすぐに、ジェスチャーが検出されるたびに通知の受信が開始されます。
touchPoint は、ジェスチャーが発生した MapView 座標を指定します。mapView.viewToGeoCoordinates(viewCoordinates: origin) を呼び出すことで、ピクセルを地理座標に変換できます (上を参照)。
同様に、リッスンを停止するには以下を呼び出します。
mapView.gestures.tapDelegate = nil
連続ジェスチャー (長押し、ピンチ、パン、2 本指のパンなど) の場合、ジェスチャー状態 begin はジェスチャーが検知されたことを示します。指が画面に触れている間は、指が離れたことを示す end 状態か、ジェスチャー検出がキャンセルされたことを示す cancel 状態になるまで、update 状態を受け取ることがあります。
// Conform to the LongPressDelegate protocol.
func onLongPress(state: heresdk.GestureState, origin: Point2D) {
if (state == .begin) {
let geoCoordinates = mapView.viewToGeoCoordinates(viewCoordinates: origin)
print("LongPress detected at: \(String(describing: geoCoordinates))")
}
if (state == .update) {
let geoCoordinates = mapView.viewToGeoCoordinates(viewCoordinates: origin)
print("LongPress update at: \(String(describing: geoCoordinates))")
}
if (state == .end) {
let geoCoordinates = mapView.viewToGeoCoordinates(viewCoordinates: origin)
print("LongPress finger lifted at: \(String(describing: geoCoordinates))")
}
if (state == .cancel) {
print("Map view lost focus. Maybe a modal dialog is shown or the app is sent to background.")
}
}
たとえば、長押しイベントが検知された後、ユーザーは指を画面上に置いたままにしたり、指を動かしたりすることがあります。ただし、長押しジェスチャーが検知された時点をマークするのは begin イベントのみです。
長押しジェスチャーは、地図にマップマーカーを配置する際に便利です。この例は「Search」サンプルアプリで確認できます。このサンプルアプリは、優先するプラットフォームのGitHubにあります。
非連続ジェスチャー (タップ、ダブル タップ、2 本指のタップなど) では、GestureState はジェスチャーの処理には必要ありません。
注パン ジェスチャーがスワイプとなり、地図が動く可能性があります。ジェスチャーがすでに終了し、すべての指が離れているにもかかわらず、地図は動き続けます。地図の移動が終了したことを検知するには、
MapIdleListenerを使用できます。これはMapViewインスタンスから取得できるHereMapインスタンスに追加できます。ピンチによる回転ジェスチャーを開始すると、パンのジェスチャーはキャンセルされます。ピンチ回転ジェスチャー中に2本指のパン操作が検出されると、パンジェスチャーが開始され、パンとピンチ回転の両方のイベントが発生します。
コードスニペットとその他の使用法の例は、GitHubで「Gestures」サンプルアプリの一部として入手できます。
マップアクションを制御する
デリゲートを設定しても、ジェスチャーのデフォルトの地図動作には影響しません。独立して制御できます。デフォルトでは、地図をダブル タップしたときのズーム インなどの標準的な動作は、すべて有効になっています。
たとえば、ダブル タップ (ズーム イン) および 2 本指のタップ (ズーム アウト) のデフォルトのマップ ジェスチャー動作を無効にするには、次のようにします。
mapView.gestures.disableDefaultAction(forGesture: .doubleTap)
mapView.gestures.disableDefaultAction(forGesture: .twoFingerTap)
デフォルトのマップ アクションを無効にしても、ジェスチャー イベントをリッスンできます。これは、ジェスチャーのデフォルト アクションをオフにして独自のズーム動作を実装する場合などに役立ちます。タップと長押しを除くすべてのジェスチャーには、デフォルトのマップ アクションが用意されています。詳細については、上の概要を参照してください。
デフォルトのマップ ジェスチャー動作を戻すには、次の呼び出しを行います。
mapView.gestures.enableDefaultAction(forGesture: .doubleTap)
mapView.gestures.enableDefaultAction(forGesture: .twoFingerTap)
マップ アクションをカスタマイズする
すでに見てきたように、デフォルトでは、ダブル タップ ジェスチャーにより地図を非連続的にズーム インできます (市区町村レベルから番地レベルに近づくなど)。このようなデフォルトのマップ ジェスチャー アクションを無効にして、独自の動作を実装したり、既存の動作に必要なアクションを追加したりできます。
注必要に応じて、プラットフォームのジェスチャー処理と HERE SDK のジェスチャー検知を組み合わせることもできます。HERE SDK は、利便性を高めるために一般的なマップ ジェスチャーに重点を絞っているため、すべての種類の細かいジェスチャー イベントは用意していません。より詳細な制御が必要な場合は、HERE SDK で利用できるジェスチャー処理とネイティブのジェスチャー検知をいつでも組み合わせることができます。
以下に、カスタム ズーム アニメーションを有効にする方法の例を示します。
カスタムズーム動作を追加する
このチュートリアルでは、マップのカスタム ズーム動作を実装する方法を説明します。具体的には、ユーザーがダブルタップまたは 2 本指のタップ ジェスチャーを実行した後、マップを徐々に拡大または縮小できるようにします。ズーム アニメーションは、しばらくするとスムーズに減速します。
アニメーションから始めましょう。これを行うには、Apple の CADisplayLink を使用して、ディスプレイのリフレッシュ レートとアニメーションを同期させることができます。
便宜的に GestureMapAnimator という名前の新しいクラスを作成します。このクラスはジェスチャーに関連するすべてのアニメーションを処理します。地図はカメラを介してズームする必要があるため、地図の MapCamera インスタンスへの参照が必要です。
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 つの非連続的なステップとしてズーム イン/outし、中間ステップはありません。
必要なジェスチャー イベントをつなげましょう。
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(origin)
}
// Conform to the TwoFingerTapDelegate protocol.
func onTwoFingerTap(origin: Point2D) {
// Start our custom zoom out animation.
gestureMapAnimator.zoomOut(origin)
}
内容は明瞭です。2 つのジェスチャー イベントをリッスンするのみです。デフォルトのズーム動作が事前に無効になっていることを確認する必要があります。上の zoomIn() および zoomOut() のメソッドは、以下に示す GestureMapAnimator の新しいメソッドにつながります。
また、2 つの定数を定義することで、ズーム アニメーションの開始値 startZoomVelocity と、現在の startZoomVelocity 値が時間の経過とともに減少する量 zoomDelta を指定します。startZoomAnimation() メソッドにより、実行ループが次のように開始されます。
private let startZoomVelocity: Double = 0.1
private let zoomDelta: Double = 0.005
// Starts the zoom in animation.
func zoomIn(_ origin: Point2D) {
zoomOrigin = origin
isZoomIn = true
startZoomAnimation()
}
// Starts the zoom out animation.
func zoomOut(_ origin: Point2D) {
zoomOrigin = origin
isZoomIn = false
startZoomAnimation()
}
private func startZoomAnimation() {
stopAnimations()
zoomVelocity = startZoomVelocity
displayLinkZoom.isPaused = false
displayLinkZoom.add(to: .current, forMode: .common)
}
zoomOrigin を保存して、地図のどこでズーム インまたはズーム アウトすべきかを通知します。
フラグ isZoomIn を使用すると、1 つのメソッドでズーム コードを処理できるようになります。アニメーションを開始するには、displayLinkZoom ループの一時停止を解除し、対応する selector のメソッド animatorLoopZoom() でズーム値を更新します。このメソッドは、以前説明した stopAnimations() メソッドを呼び出すことで、displayLinkZoom が再び一時停止されるまで、定期的に呼び出されます。
@objc private func animatorLoopZoom() {
var zoomFactor: Double = 1
zoomFactor = isZoomIn ? zoomFactor + zoomVelocity : zoomFactor - zoomVelocity;
// zoomFactor values > 1 will zoom in and values < 1 will zoom out.
camera.zoomBy(zoomFactor, around: zoomOrigin)
zoomVelocity = zoomVelocity - zoomDelta
if (zoomVelocity <= 0) {
stopAnimations()
}
}
上記の単純なアルゴリズムでは、zoomVelocity 値は 0.1 から 0 に近い値で補間されます。zoomVelocity は地図を徐々にズームできるアニメーション値です。開始値では startZoomVelocity は 0.1 として定義されます。zoomVelocity は引数として使用し、ズーム率を設定します。zoomVelocity がゆっくりと 0 に近づくにつれて、結果のズーム ステップは小さくなります。
上では、zoomOriginに保存したタッチ原点を使用して、指が画面に触れたポイントにズームインします。ズーム アウトする場合、これは 2 本指のタップ ジェスチャーの間のポイントになります。次に、カメラの zoomBy() メソッドが非連続的なズーム ステップを指定された位置で実行します。
ブール型フラグ isZoomIn は、地図をズーム インまたはズーム アウトすべきかを示します。これにより、どちらのズームの場合でもこのコードを使用できます。唯一の違いは、現在のズーム率に対してアニメーション値 zoomVelocity が加算または減算される点です。
ズーム率は、地図をどの程度ズーム インまたはズーム アウトするかを指定します。ズーム率が 1 の場合、現在のズーム レベルは変更されません。
それでは、説明はここまでです。上のコード スニペットは、ニーズに合わせて自由に変更してください。たとえば、さまざまな Interpolator、異なるアニメーション値、または異なるアニメーションの継続時間を使用して試してみましょう。
サンプルアプリを試す
上記のコードスニペットのほとんどは、「Gestures」サンプルアプリで利用できます。このサンプルアプリは、GitHubでお好みのプラットフォームのものを見つけることができます。
7 日前の更新

















