画像をSwiftUIで拡大したりできるようにする方法!!

ジョーンズ教授: それでは具体的なコードを使って、1行ずつ解説していきましょう。

struct PhotoView: View {
    @State private var scale: CGFloat = 1.0
    @State private var lastScale: CGFloat = 1.0
    @State private var currentPosition: CGSize = .zero
    @State private var newPosition: CGSize = .zero
    @GestureState private var gestureScale: CGFloat = 1.0

    var body: some View {

マイケルくん: まずはPhotoViewという構造体を定義していますね。

ジョーンズ教授: そうです。PhotoViewはSwiftUIのビューとして使用するためのものです。次に@State属性を使っていくつかの状態変数を定義しています。scaleは現在の拡大率を表し、lastScaleは直前の拡大率を保持します。currentPositionnewPositionは写真の現在の位置を表すために使用され、gestureScaleはジェスチャーのスケールを表します。

let dragGesture = DragGesture()
            .onChanged { value in
                self.currentPosition = CGSize(
                    width: value.translation.width + self.newPosition.width,
                    height: value.translation.height + self.newPosition.height
                )
            }
            .onEnded { value in
                self.currentPosition = CGSize(
                    width: value.translation.width + self.newPosition.width,
                    height: value.translation.height + self.newPosition.height
                )
                self.newPosition = self.currentPosition
            }

マイケルくん: dragGestureという変数が定義されていますね。これはドラッグ操作に関するジェスチャーの設定ですか?

ジョーンズ教授: そうです。DragGestureを使ってドラッグ操作を検知し、その位置情報を元にcurrentPositionを更新しています。onChangedクロージャではドラッグ中の位置情報を受け取り、currentPositionを更新します。onEndedクロージャではドラッグ操作が終了した際に最終的な位置情報をnewPositionに保存し、currentPositionも同じ値に更新します。

let magnificationGesture = MagnificationGesture()
            .onChanged { value in
                self.scale = self.lastScale * value
            }
            .onEnded { value in
                self.lastScale = self.scale
            }

マイケルくん: 次にmagnificationGestureという変数が定義されていますね。これは拡大縮小のジェスチャーの設定ですか?

ジョーンズ教授: その通りです。MagnificationGestureを使ってピンチ操作(2本の指でのつまむ操作)を検知し、そのスケール情報を元にscaleを更新しています。onChangedクロージャではスケール変更中の情報を受け取り、scaleを更新します。onEndedクロージャではスケール変更が終了した際に最終的な拡大率をlastScaleに保存します。

let combinedGesture = magnificationGesture.simultaneously(with: dragGesture)

        return Image(systemName: "books.vertical.fill")
            .resizable()
            .scaledToFit()
            .scaleEffect(scale * gestureScale)
            .offset(x: currentPosition.width, y: currentPosition.height)
            .gesture(combinedGesture)
            .animation(.easeInOut)
            .onTapGesture {
                // タップ時の処理を追加
            }
    }
}

ジョーンズ教授: 最後にcombinedGestureという変数を定義しています。これは拡大縮小とドラッグのジェスチャーを同時に検知するための設定です。magnificationGesturedragGesturesimultaneously(with:)メソッドで組み合わせています。

Image(systemName: "books.vertical.fill")で本のアイコンを表示し、.resizable().scaledToFit()で画像のリサイズとアスペクト比の維持を設定しています。.scaleEffect(scale * gestureScale)で拡大率を適用し、.offset(x: currentPosition.width, y: currentPosition.height)で位置を設定しています。

最後に.gesture(combinedGesture)でジェスチャーをビューに適用し、.animation(.easeInOut)でアニメーション効果を追加しています。また、.onTapGestureではタップ時の処理を追加できます。

マイケルくん: なるほど、combinedGestureを使って拡大縮小とドラッグのジェスチャーを同時に検知しているんですね。そして、Imageビューで本のアイコンを表示し、その後にリサイズやアスペクト比の維持、拡大率や位置の設定を行っています。

ジョーンズ教授: その通りです。そして、最後にcombinedGestureをビューに適用しています。アニメーション効果も追加されており、.onTapGestureではタップ時の処理を追加できるようになっています。

マイケルくん: なるほど、このPhotoViewは写真の表示と操作を行うためのビューとして使われるんですね。他のビューに組み込んで利用できるのですか?

ジョーンズ教授: はい、正確に言うとPhotoView自体は他のビューに組み込むための部品として使われます。例えば、ContentViewなどの親ビューにPhotoViewを組み込んで利用することができます。

マイケルくん: なるほど、親ビューに組み込んで使うんですね。PhotoViewを使って写真の表示と操作を行うアプリを作ることができそうです。

ジョーンズ教授: そうですね。PhotoViewを使えば、写真の拡大縮小やドラッグ操作などを簡単に実装することができます。自分のアプリに合わせてサイズや表示内容を調整してみてください。

マイケルくん: ありがとうございます!これを使って面白いアプリを作ってみたいと思います!

ジョーンズ教授: どういたしまして!頑張ってくださいね。質問があればいつでも聞いてください。

全体コード

struct PhotoView: View {
    @State private var scale: CGFloat = 1.0
    @State private var lastScale: CGFloat = 1.0
    @State private var currentPosition: CGSize = .zero
    @State private var newPosition: CGSize = .zero
    @GestureState private var gestureScale: CGFloat = 1.0

    var body: some View {
        let dragGesture = DragGesture()
            .onChanged { value in
                self.currentPosition = CGSize(
                    width: value.translation.width + self.newPosition.width,
                    height: value.translation.height + self.newPosition.height
                )
            }
            .onEnded { value in
                self.currentPosition = CGSize(
                    width: value.translation.width + self.newPosition.width,
                    height: value.translation.height + self.newPosition.height
                )
                self.newPosition = self.currentPosition
            }

        let magnificationGesture = MagnificationGesture()
            .onChanged { value in
                self.scale = self.lastScale * value
            }
            .onEnded { value in
                self.lastScale = self.scale
            }

        let combinedGesture = magnificationGesture.simultaneously(with: dragGesture)

        return Image(systemName: "books.vertical.fill")
            .resizable()
            .scaledToFit()
            .scaleEffect(scale * gestureScale)
            .offset(x: currentPosition.width, y: currentPosition.height)
            .gesture(combinedGesture)
            .animation(.easeInOut)
            .onTapGesture {
                // タップ時の処理を追加
            }
    }
}

struct ContentView: View {
    var body: some View {
        VStack {
            PhotoView()
                .frame(width: 300, height: 300) // 好きなサイズに調整してください
        }
    }
}

この記事が気に入ったらサポートをしてみませんか?