ZStackにおけるViewについて考察する(004,ジェスチャのコードをメソッド化する)

この記事は下記の続きです。下記の記事のコードではタップとドラッグによる座標検出のところのコードが同じなので、この記事ではそれをメソッド化して見通しがよくなる工夫をします。

ZStackにおけるViewについて考察する(003,タップだけでなくドラッグにも対応)

ジェスチャについて、タップだけでなくドラッグにも対応させる方法を紹介します。

コードの見通しがよくなるだけで、実行結果は同じなので iPhone シミュレータの実行画像は省略します。

いままでの記事では、座標を ( TapX, TapY ) と ( InsideRectTapX, InsideRectTapY ) として4つの CGFloat として変数宣言していましたが、本記事では管理する変数を減らすために Tap という CGPoint 型の変数、InsideRectTap という CGPoint 型の変数として宣言しました。17、18行目がそれにあたります。

90〜127行目が、タップした座標を取得するメソッドです。これを使うことで、onTapGesture や DragGesture のところのコードが、かなり見通しよくなっています。

getInsideRectTapPoint() メソッドは引数ラベルを省略可能とできるようにアンダスコアで定義してあります。

81行目、Text の色を FlagInsideRect の true/false を評価して、色を変化させるようにしています。

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 Tap = CGPoint( x: 0.0, y: 0.0 )
    @State var InsideRectTap = CGPoint( x: 0.0, y: 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
                    Tap = tap
                    let rct = CGSize( width: RECT_W, height: RECT_H )
                    getInsideRectTapPoint( &InsideRectTap, &FlagInsideRect, Tap, rct )
                }
                .gesture(
                    DragGesture( minimumDistance: 0 )
                        .onChanged{ tap in
                            Tap = tap.location // ここは tap の location.
                            let rct = CGSize( width: RECT_W, height: RECT_H )
                            getInsideRectTapPoint( &InsideRectTap, &FlagInsideRect, Tap, rct )
                        }
                        .onEnded { tap in
                            Tap = tap.location // ここは tap の location.
                            let rct = CGSize( width: RECT_W, height: RECT_H )
                            getInsideRectTapPoint( &InsideRectTap, &FlagInsideRect, Tap, rct )
                        }
                    )

            // 十字カーソルを描画する.
            Path { path in

                let the_x = InsideRectTap.x
                let the_y = InsideRectTap.y

                // 始点から終点にむかって線分を引く(垂直線).
                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 )", Tap.x, Tap.y ))
                    .foregroundColor( COLOR_TEXT )
                Text( String( format: "InsideRectTapXY( %.1f, %.1f )", InsideRectTap.x, InsideRectTap.y ))
                    .foregroundColor( COLOR_TEXT )
                Text( String( format: "FlagInsideRect is %d", FlagInsideRect ))
                    .foregroundColor( FlagInsideRect ? COLOR_INSIDE : COLOR_OUTSIDE )

            }

        }

    }

    // タップした座標が矩形内部に入っているか確認して矩形内部の座標として取得するメソッド.
    func getInsideRectTapPoint(
            _ inside_rect_tap: inout CGPoint,
            _ flag_inside_rect: inout Bool,
            _ tap: CGPoint,
            _ rect: CGSize
            )->Void{

        // いったんそのままタップ座標を代入する.
        inside_rect_tap.x = tap.x
        inside_rect_tap.y = tap.y

        // いったんフラグを真にしておく.
        flag_inside_rect = true

        // Rectangleの内側か外側か判定して inside_rect_tap.x と .y を調整する.
        if ( inside_rect_tap.x < 0.0 ){
            inside_rect_tap.x = 0.0
            flag_inside_rect = false
        }

        if ( inside_rect_tap.y < 0.0 ){
            inside_rect_tap.y = 0.0
            flag_inside_rect = false
        }

        if ( inside_rect_tap.x >= rect.width ){
            inside_rect_tap.x = rect.width - 1
            flag_inside_rect = false
        }

        if ( inside_rect_tap.y >= rect.height ){
            inside_rect_tap.y = rect.height - 1
            flag_inside_rect = false
        }

        return

    }

}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}