iOS17で追加されたMagnifyGestureを使ってSwiftUIで画像をピンチイン・ピンチアウトする

こんにちは!亀山です。急に寒くなりました。今日は、feather for Mastodonで実装したズーム可能なビューについて詳しく取り上げます。皆さんがコードを書く際の参考になれば幸いです。

ライブラリの紹介

この機能を簡単に利用できるように、ZoomableというライブラリをGitHubに公開しました。興味があれば、こちらのリポジトリをチェックしてみてください。

github.com

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関数は、ジェスチャが終了したときに呼び出されます。この関数内でtransformlastTransformを更新し、ビューの変形を最終的に確定します。

animatableTransformEffect は CGAffineTransform の移動・拡大の変化をアニメーションに対応させたものです。transformEffect はアニメーションを行わないため、scaleEffect と offset を使っています。

4. まとめ

SwiftUIでのズームとパンの機能は、上記のようなステップで簡単に実装できます。このModifierを使用することで、リッチなユーザー体験を提供するアプリケーションを作成することができます。

質問やフィードバックがあれば、XMastodonにてお気軽にお知らせください。次回も、役立つ技術的なトピックでお会いしましょう!