先日の [SwiftUI]Image上でTextをドラッグして動かす でImage上のTextをドラッグして動かす方法を説明したのですが、実は、以下の問題点があります。
- ドラッグし終わったときのPositionを参照しないので、2回目以降のドラッグ開始時にTextが急激に移動する
問題の発生原因
この問題(2回目以降のドラッグ開始時にTextが急激に移動する)は、サンプルのDrag処理では、Textのoffsetはドラッグ開始時には(0,0)であることを想定して書かれているので、offsetが(0,0)で無い位置にあるときにドラッグを開始すると、offsetが(0,0)の位置にあったこと前提で移動処理をします。そのため、ドラッグ開始時に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等を変換しつつ計算してくれるライブラリが欲しくなります。
Sponsor Link
[…] このような表示要素に対して、ドラッグ等のGestureを紐づけたとします。(おおよそ、先日のPostからコードを持ってきています) […]