ZStackにおけるViewについて考察する(003,タップだけでなくドラッグにも対応)
この記事は下記の記事の続きです。画面上のタップだけではなく、画面上を指で滑らせるドラッグ動作にも反応して座標を取得する方法を紹介します。
タップジェスチャに記述しているコードと全く同じものを、DragGesture の .onChanged と .onEnded に書き込んでください。
75、76行目が、ドラッグした座標を取得しているところです、tap.location で取得できます。
109、110行目も、ドラッグした座標を取得しているところです、tap.location で取得できます。
タップした座標は、下記のコードでは39、40行目で取得していますが、こちらは tap だけで座標を取得しています。tap.location と tap で微妙な違いですがビルドが通らなくて困ってしまいそうなところなのでご注意ください。
72行目が画面上で指を滑らせたときのドラッグの反応性を決定しています。minimumDistance を 0 にしているのは、指をすべらせてすぐにドラッグに反応させたいからです。この値を 100 などの極端な値に変更して試してみてください。画面上で距離が 100.0 以上指を接触させたまま動かないとドラッグ動作に反応しません。
ツムツムのようなゲームはドラッグ動作がキモなので minimumDistance を 0 にしないといけないでしょう。
import SwiftUI
struct ContentView: View {
let RECT_W: CGFloat = 320
let RECT_H: CGFloat = 640
let COLOR_RECT: Color = Color.blue
let COLOR_TEXT: Color = Color.white
let COLOR_INSIDE: Color = Color( red: 0x00, green: 0xff, blue: 0x00, opacity: 0xff )
let COLOR_OUTSIDE: Color = Color( red: 0xff, green: 0x00, blue: 0x00, opacity: 0xff )
let CURSOR_COLOR: Color = Color.yellow
let CURSOR_LINE_WIDTH: CGFloat = 3.0
@State var TapX: CGFloat = 0.0
@State var TapY: CGFloat = 0.0
@State var InsideRectTapX: CGFloat = 0.0
@State var InsideRectTapY: CGFloat = 0.0
@State var FlagInsideRect = true
var body: some View {
ZStack{
// 最背面.
Rectangle()
.foregroundColor( FlagInsideRect ? COLOR_INSIDE : COLOR_OUTSIDE )
// 中層面.
Rectangle()
.frame( width: RECT_W, height: RECT_H ) // サイズを指定する.
.foregroundColor( COLOR_RECT ) // 色を指定する.
.onTapGesture {
tap in
TapX = tap.x
TapY = tap.y
// いったんそのままタップ座標を代入する.
InsideRectTapX = TapX
InsideRectTapY = TapY
// いったんフラグを真にしておく.
FlagInsideRect = true
// Rectangleの内側か外側か判定して InsideRectTapXとY を調整する.
if ( InsideRectTapX < 0.0 ){
InsideRectTapX = 0.0;
FlagInsideRect = false
}
if ( InsideRectTapY < 0.0 ){
InsideRectTapY = 0.0
FlagInsideRect = false
}
if ( InsideRectTapX >= RECT_W ){
InsideRectTapX = RECT_W - 1
FlagInsideRect = false
}
if ( InsideRectTapY >= RECT_H ){
InsideRectTapY = RECT_H - 1
FlagInsideRect = false
}
}
.gesture(
DragGesture( minimumDistance: 0 )
.onChanged{ tap in
TapX = tap.location.x
TapY = tap.location.y
// いったんそのままタップ座標を代入する.
InsideRectTapX = TapX
InsideRectTapY = TapY
// いったんフラグを真にしておく.
FlagInsideRect = true
// Rectangleの内側か外側か判定して InsideRectTapXとY を調整する.
if ( InsideRectTapX < 0.0 ){
InsideRectTapX = 0.0;
FlagInsideRect = false
}
if ( InsideRectTapY < 0.0 ){
InsideRectTapY = 0.0
FlagInsideRect = false
}
if ( InsideRectTapX >= RECT_W ){
InsideRectTapX = RECT_W - 1
FlagInsideRect = false
}
if ( InsideRectTapY >= RECT_H ){
InsideRectTapY = RECT_H - 1
FlagInsideRect = false
}
}
.onEnded { tap in
TapX = tap.location.x
TapY = tap.location.y
// いったんそのままタップ座標を代入する.
InsideRectTapX = TapX
InsideRectTapY = TapY
// いったんフラグを真にしておく.
FlagInsideRect = true
// Rectangleの内側か外側か判定して InsideRectTapXとY を調整する.
if ( InsideRectTapX < 0.0 ){
InsideRectTapX = 0.0;
FlagInsideRect = false
}
if ( InsideRectTapY < 0.0 ){
InsideRectTapY = 0.0
FlagInsideRect = false
}
if ( InsideRectTapX >= RECT_W ){
InsideRectTapX = RECT_W - 1
FlagInsideRect = false
}
if ( InsideRectTapY >= RECT_H ){
InsideRectTapY = RECT_H - 1
FlagInsideRect = false
}
}
)
// 十字カーソルを描画する.
Path { path in
let the_x = InsideRectTapX
let the_y = InsideRectTapY
// 始点から終点にむかって線分を引く(垂直線).
path.move(to: CGPoint(x: the_x, y: 0 ))
path.addLine(to: CGPoint(x: the_x, y: RECT_H ))
// 始点から終点にむかって線分を引く(水平線).
path.move(to: CGPoint(x: 0, y: the_y ))
path.addLine(to: CGPoint(x: RECT_W, y: the_y ))
}
.stroke( CURSOR_COLOR, lineWidth: CURSOR_LINE_WIDTH )
.frame( width: RECT_W, height: RECT_H )
// 最前面.
VStack{
Text( String( format: "Rectangle WH %.1f * %.1f", RECT_W, RECT_H ))
.foregroundColor( COLOR_TEXT )
Text( String( format: "TapXY( %.1f, %.1f )", TapX, TapY ))
.foregroundColor( COLOR_TEXT )
Text( String( format: "InsideRectTapXY( %.1f, %.1f )", InsideRectTapX, InsideRectTapY ))
.foregroundColor( COLOR_TEXT )
Text( String( format: "FlagInsideRect is %d", FlagInsideRect ))
.foregroundColor( COLOR_TEXT )
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}