ZStackにおけるViewについて考察する(000,超単純コード)

Swiftでは Rectangle や Image や Button や Text など、画面に配置する部品は View と呼ばれ、システムから管理されます。

Windows C#において Shape や PictureBox や Button や Text など、それはすべてウインドウを継承して形を変えたものという概念と同じです。

Image などの View は、ZStack によって位置管理されることが多いので、これについて考察してみます。下記のコードは ZStack として背面に Rectangle を配置して、前面に VStack によってまとめられた変数表示用の Text を配置しています。

ZStack の前面の VStack の意味がわかりづらかったら、VStack を使わずに3個 Text を並べたら VStack の効果がわかりやすいと思います。

シミュレータによる実行結果

ZStack の上に View を配置すると必ず真ん中に配置されて重ねて表示されます。Rectangle にはタップした座標がとれるように .onTapGesture モディファイアをつけておきました。Rectangle の内側をタップすると FlagInsideRect is 1 と表示されて、やや外側をタップすると FlagInsideRect is 0 と表示されます。

機種によっては、Rectangle が画面からはみ出す可能性もあります。その場合は RECT_W と RECT_H の値を調整して、Rectangle のタップ座標の内側、外側の確認をしてください。

SwiftUI の .onTapGesture モディファイアは、ビュー(この例では Rectangle )の内部領域に対して反応するように設計されています。しかし、コードを実行してみればわかると思いますが、ビューのやや外側でもジェスチャーを検出します。

この現象は検出された座標がマイナスになるときが確認しやすいです。なぜそのような動作になるかは理由はわかりません。指先の太さのことを考慮しているのでしょうか。Rectangle から明らかに遠い場所をタップしたら .onTapGesgure は反応しません。

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

    @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()
                .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
                    }

                }

            // 前面.
            VStack{

                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()
    }
}