ZStackにおけるViewについて考察する(002,タップ座標を十字カーソルで表示)

この記事は下記の記事の続きです。

ZStackにおけるViewについて考察する(001,内側外側で背景色を変える)

ZStack の最背面の色を変更して、所望のビューのタップジェスチャの反応を調べます。

タップした座標を十字線を使って確認しやすくしてみます。

Fig. 1 Rectangle の内側をタップしたとき
Fig. 2 Rectangle の右下のやや外側をタップしたとき

ZStack でタップを検出する Rectangle の上に、Path というビューを置いて、そこに十字線を描画します。

ZStackにおけるビューの配置は下記のようになっています。

最背面から

(0) タップの内側、外側を知らせるために色が変わる Rectangle
(1) タップされた座標を知るための onTapGesture モディファイアが定義された Rectangle
(2) タップされた座標の十字線を描画するための Path
(3) 内部変数を確認するための Text

という順に重ねています。

88行目に示すように、Path の描画領域の大きさは .frame モディファイアを使って上記の (1) の Rectangle と同じ大きさに設定します。Path の位置は、Rectangle や Text と同様に ZStack で管理されているので画面の真ん中に配置されます。

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
                    }

                }

            // 十字カーソルを描画する.
            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()
    }
}