概要:
DragGesture、RotationGestureをソースコードから学びます。
iPadのアプリSwift Playgroundsの中の「ジェスチャの認識」のソースコードで説明します。
「ジェスチャの認識」のファイル構成やTapGesture/LongPressGestureに関しては前回の記事で書いていますので、よろしければご参照ください。
DragGesture
これはGestureの中で最もマスターしておきたいところですね。
以下がDragViewのソースコードです。
struct DragView: View {
private let circleSize: CGFloat = 100
@State private var offset = CGSize.zero
var dragGesture: some Gesture {
DragGesture()
.onChanged { value in
offset = CGSize(width: value.startLocation.x + value.translation.width - circleSize/2,
height: value.startLocation.y + value.translation.height - circleSize/2)
}
}
var body: some View {
VStack {
Text("Use one finger to drag the circle around")
Spacer()
Circle()
.foregroundColor(.teal)
.frame(width: circleSize, height: circleSize)
.offset(offset)
.gesture(dragGesture)
Spacer()
}
.navigationTitle("Drag")
.padding()
.toolbar {
Button("Reset") {
offset = .zero
}
}
}
}
Viewの構成はTapGestureやLongPressGestureと同じですね。
ここでは.onChangedを使っています。押されている間は移動(座標の変化)があるとvalue構造体に最新の値がセットされて、常に呼ばれるイメージです。
value構造体は主に以下のようなプロパティを持っています。
var startLocation | CGPoint | ドラッグをスタートした時の位置 |
var location | CGPoint | ドラッグ中の現在の位置 |
var translation | CGSize | スタート位置から現在位置の移動距離 |
注意しなければならないのは、startLocationやlocationはデバイスの画面での座標ではなく、Gestureを設定したViewの左上からの座標になります。
今回の場合、初期に円の中心をタッチすると(50,50)の座標がstartLocationに入ってきます。
なので計算式では半円分の値を引いているのですね。
.onChanged { value in
offset = CGSize(width: value.startLocation.x + value.translation.width - circleSize/2,
height: value.startLocation.y + value.translation.height - circleSize/2)
}
ただ、この計算式だと円の端をドラッグすると不自然な動きです(笑)
参考までに、これを改善したソースコードを紹介しておきます。
struct DragView: View {
private let circleSize: CGFloat = 100
@State private var offset = CGSize.zero
@State private var lastOffset = CGSize.zero // ← 追加
var dragGesture: some Gesture {
DragGesture()
.onChanged { value in
offset = CGSize(width: value.translation.width + lastOffset.width, // 変更
height: value.translation.height + lastOffset.height) // 変更
}
.onEnded { _ in // ← 追加
lastOffset = offset. // ← 追加
} // ← 追加
}
// 以下省略
ドラッグ終了時にその時のoffsetを保持し、次回はそこからの差分にしています。
DragGestureにPathを組み合わせる
同じDragGestureを使っているSingleLineのViewを説明します。
アプリ内ではLIne Drawingという名称になっている部分です。
ソースコードは以下の通りです。
struct SingleLine: View {
@State var lineStart = CGPoint.zero
@State var lineEnd = CGPoint.zero
var lineDrawingGesture: some Gesture {
DragGesture()
.onChanged { value in
lineStart = value.startLocation
lineEnd = value.location
}
.onEnded { value in
lineEnd = value.location
}
}
var body: some View {
VStack {
Text("Touch and drag to make a line")
Spacer()
Path { path in
path.move(to: lineStart)
path.addLine(to: lineEnd)
}
.stroke(Color.green, lineWidth: 8.0)
.contentShape(Rectangle())
.gesture(lineDrawingGesture)
}
.navigationTitle("Line Drawing")
.padding()
.toolbar {
Button("Reset") {
lineStart = .zero
lineEnd = .zero
}
}
}
}
onChangedは、ドラッグを開始してから終了するまで常に呼び出されます。その時の位置情報がvalue構造体にセットされて来ます。
onEndedは、ドラッグが終了する最終の時だけ呼ばれます。
SwiftUIでは、@Stateで宣言しているプロパティが変化すると、関連Viewへ通知されます。
lineStartやlineEndが変化すると、都度Pathがリアルタイムに線を描くわけです。
Pathを簡単に説明しておくと、
.moveは、描き初めの点へ「筆」を持っていく(まだ書かない)イメージ。
.addLineは、move位置から筆を下ろし、toまで線を引く。
.strokeは、線を描くときの色と幅を指定する。
.contentShapeは、描画を描き始められる場所の範囲の形を指定する。
という感じです。
色々変えて、試してみると理解が早まると思います。
RotationGesture
最後はRotationGestureを見ていきます。
リストから”Rotate”を選択した画面です。
2本指で図形を回転されることができるというアプリです。では、ソースコードを見ていきましょう。
struct RotateView: View {
@State private var rotation = Angle.zero
var rotationGesture: some Gesture {
RotationGesture()
.onChanged{ angle in
rotation = angle
}
.onEnded { angle in
rotation = angle
}
}
var body: some View {
VStack {
Text("Use two fingers to rotate the box")
Spacer()
Rectangle()
.foregroundColor(.red)
.frame(width: 225, height: 225)
.rotationEffect(rotation)
.gesture(rotationGesture)
Spacer()
}
.navigationTitle("Rotate")
.padding()
.toolbar {
Button("Reset") {
rotation = .zero
}
}
}
}
これもいたってシンプルですね。RotationGestureのonChangedやonEndedで受け渡されるのはangleです。それをRectangleのrotationEffectに渡しているだけです。
Angleはラジアンで扱います。値は0から2πということになります。onChangedやonEndedで出てくるのも、rotationEffectに渡すのもラジアンです。
onChangedで渡されるangleはその都度の途中経過、
onEndedで渡されるangleが最終確定値、
になりますので、角度を正しく読みたい場合はonEndedの値を使います。
最後に
iPadのアプリSwift Playgroundsにある「ジェスチャの認識」というアプリのソースコードで、ジェスチャ系の解説してきました。
ジェスチャの基本的なところが、とてもわかりやすく学べるサンプルプログラムだと思います。
ここまで読んでいただき、ありがとうございました。
ご意見、ご指摘等ありましたら、コメント頂けると大変嬉しいです。
コメント