[SwiftUI] ドラッグによりViewを移動させるときに気をつけること

先日の [SwiftUI]Image上でTextをドラッグして動かす でImage上のTextをドラッグして動かす方法を説明したのですが、実は、以下の問題点があります。

  • ドラッグし終わったときのPositionを参照しないので、2回目以降のドラッグ開始時にTextが急激に移動する

問題の発生原因

この問題(2回目以降のドラッグ開始時にTextが急激に移動する)は、サンプルのDrag処理では、Textのoffsetはドラッグ開始時には(0,0)であることを想定して書かれているので、offsetが(0,0)で無い位置にあるときにドラッグを開始すると、offsetが(0,0)の位置にあったこと前提で移動処理をします。そのため、ドラッグ開始時にTextが急激に移動しているように見えます。

図にすると以下のような形です。

MouseDragとText移動

ドラッグ時にズレが発生しないための方法

以下のことが必要です。

  • ドラッグ開始時にどこにTextがあったか覚えておく
  • 移動させるときには、ドラッグ開始時の位置からの相対位置で移動させる

ドラッグ開始時のText位置と移動計算

onChangeは、ドラッグ開始時にも呼ばれますので、ドラッグ開始時に、自Viewの中に、Textのoffsetを保持します。(dragStartPosition)

移動継続中にもonChangeは呼ばれますので、開始時のみTextのoffsetを保持するような変数も導入します。(isDragging)

移動中のoffsetの計算も、ドラッグ開始時の位置情報とtranslation情報を使って、新しいoffsetを計算します。

コードにすると、以下のようになります。

コード

struct PrintView: View {
    @State private var textOffset: CGSize = CGSize.zero
    @State var dragStartPosition: CGPoint = CGPoint.zero
    @State var isDragging:Bool = false

    @Binding var baseImage: NSImage
    var body: some View {
        ZStack {
            Image(nsImage: self.baseImage)
                .resizable()
                .scaledToFit()
            Text("Hello, World!")
                .background(Color.white)
                .offset(textOffset)
                .gesture(DragGesture()
                    .onChanged { gesture in
                        if self.isDragging == false {
                            self.dragStartPosition = CGPoint(x: self.textOffset.width, y: self.textOffset.height)
                            self.isDragging = true
                        }
                        self.textOffset =  CGSize(width: gesture.translation.width + self.dragStartPosition.x,
                                                 height: gesture.translation.height + self.dragStartPosition.y)
                    }
                    .onEnded { gesture in
                        self.isDragging = false
                    })
        }
    }
}

まとめ

細かい処理追加ですが、上記の処理を追加することで、直感的なドラッグ処理になります。

MEMO
上記のようなコードを書いていると、CGRectやCGPoint等を変換しつつ計算してくれるライブラリが欲しくなります。

1 COMMENT

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です