こんにちは!亀山です。急に寒くなりました。今日は、feather for Mastodonで実装したズーム可能なビューについて詳しく取り上げます。皆さんがコードを書く際の参考になれば幸いです。
ライブラリの紹介
この機能を簡単に利用できるように、Zoomable
というライブラリをGitHubに公開しました。興味があれば、こちらのリポジトリをチェックしてみてください。
1. ZoomableModifier
の導入
Zoomableの核となる部分は、ZoomableModifier
というViewModifierです。このModifierは、SwiftUIビューにピンチジェスチャやダブルタップジェスチャを追加して、ビューのズームイン・アウトを可能にします。
主要な変数として、以下のものがあります:
transform
: 現在の変形を示すもの。lastTransform
: 最後の変形を示すもの。ジェスチャが終了したときにこれを更新します。imageSize
: ビューの元のサイズ。ズームやパンの制限を実装する際に使用します。
2. ジェスチャの実装
各ジェスチャは CGAffineTransform を使ってアフィン変換として画像の移動・拡大縮小を行います。
a. ダブルタップジェスチャ
doubleTapGesture
を使用し、ユーザーがビューをダブルタップしたときにズームイン・アウトの切り替えを行います。具体的には、現在のtransform
のスケールが1.0でない場合(すでにズームされている場合)、変形をリセット(ズームアウト)します。そうでない場合、指定されたdoubleTapZoomScale
でズームインします。
b. ドラッグジェスチャ
dragGesture
は、ビューのドラッグ移動(パンニング)を実装します。重要なのは、移動量を現在のスケールで割ることで、ズームレベルに応じてパン速度を調整します。
c. ピンチジェスチャ
iOSのバージョンに応じて、oldMagnificationGesture
または magnificationGesture
を使用します。iOS 16ではMagnificationGesture
を使用しますが、ズームのアンカーポイントが取得できないため、左上に固定される問題があります。一方、iOS 17ではMagnifyGesture
が導入され、アンカーポイントが取得できるようになり、問題が改善されます。
3. 補助関数
limitTransform
関数を使用して、ビューの変形を制限します。これにより、ビューが指定された最小ズームスケール以下にならないようにし、またビューが画面外に移動しすぎないようにします。
onEndGesture
関数は、ジェスチャが終了したときに呼び出されます。この関数内でtransform
とlastTransform
を更新し、ビューの変形を最終的に確定します。
animatableTransformEffect
は CGAffineTransform の移動・拡大の変化をアニメーションに対応させたものです。transformEffect
はアニメーションを行わないため、scaleEffect と offset を使っています。
4. まとめ
SwiftUIでのズームとパンの機能は、上記のようなステップで簡単に実装できます。このModifierを使用することで、リッチなユーザー体験を提供するアプリケーションを作成することができます。
質問やフィードバックがあれば、XやMastodonにてお気軽にお知らせください。次回も、役立つ技術的なトピックでお会いしましょう!