iOS・AndroidSwift

【SwiftUI】Stack内における .position の挙動を確認する

iOS・Android
この記事は約6分で読めます。

.positionは親View座標基準で位置が決定されます。

親ビューがそのコンテンツを配置する場所をオーバーライドする修飾子となっています。

position(x:y:) | Apple Developer Documentation
Positions the center of this view at the specified coordinates in its parent’s coordinate space.

Stack内における挙動を確認します。

サンプルコード

公式の「Making fine adjustments to a view’s position」中に出てくるUIを簡易的に再現しました。

struct Crosshairs: View {
    var body: some View {
        VStack {
            Spacer()
            Spacer()
                .frame(width: 1, height: 160)
                .background(Color.black)
            Spacer()
        }
        HStack {
            Spacer()
            Spacer()
                .frame(width: 160, height: 1)
                .background(Color.black)
            Spacer()
        }
    }
}

struct ContentView: View {
    var body: some View {
        ZStack {
            Crosshairs()
            Rectangle()
                .stroke(Color.primary)
                .frame(width: 160, height: 160)
            Image(systemName: "circle")
            
        }.frame(width: 200, height: 200)
    }
}

この時点で以下のような画面になります。

Making fine adjustments to a view’s position | Apple Developer Documentation
Shift the position of a view by applying the offset or position modifier.

SwiftUIにおける座標空間

SwiftUIにおけるビューの座標空間では、x = 水平方向、y = 垂直方向を表現します。

Viewの左上の角が、(0 , 0) の地点となります。

SwiftUIにおける描画の仕組み

WWDC の Building Custom Views with SwiftUI によると,レイアウトのプロセスは次です。

  1. 親が子に表示可能サイズを提案
  2. 子の View が自身のサイズを決定
  3. 親が親の座標空間に子を配置

View によって自身のサイズの決め方は異なるということ。また、親からのサイズの提案に従わない場合もある点に注意が必要です。

ZStackにおける .position(x: 0, y: 0)

上記のコードを一部書き換えてみます。

struct ContentView: View {
    var body: some View {
        ZStack {
            Crosshairs()
            Rectangle()
                .stroke(Color.primary)
                .frame(width: 160, height: 160)
            Image(systemName: "circle")
                .position(x: 0, y: 0)
            
        }.frame(width: 200, height: 200)
            .border(.red)
    }
}

わかりやすくするため、ZStackに赤い枠線を付与しています。

結果が添付画像です。

見ての通り、親(ZStack)基準で(x: 0, y: 0)のポイントに設置されていることがわかりますね。

ZStack内に .position(x: 0, y: 0) のオブジェクトをもう一つ追加する

お星様を追加してみました。

struct ContentView: View {
    var body: some View {
        ZStack {
            Crosshairs()
            Rectangle()
                .stroke(Color.primary)
                .frame(width: 160, height: 160)
            Image(systemName: "circle")
                .position(x: 0, y: 0)
            // 追加
            Image(systemName: "star")
                .position(x: 0, y: 0)
            
        }.frame(width: 200, height: 200)
            .border(.red)
    }
}

結果は以下です。

HStack(VStack)における .position(x: 0, y: 0)

コードを下記の形に書き換えてみました。

struct ContentView: View {
    var body: some View {
        ZStack {
            Crosshairs()
            Rectangle()
                .stroke(Color.primary)
                .frame(width: 160, height: 160)
            // 書き換え
            HStack {
                Image(systemName: "circle")
                    .position(x: 0, y: 0)
                Image(systemName: "star")
                    .position(x: 0, y: 0)
            }
            
        }.frame(width: 200, height: 200)
            .border(.red)
    }
}

結果は以下です。

わかりやすくするために背景色ををつけてみましょう。

それぞれ、HStack内の自身に割り当てられた空間の座標を起点に、(x: 0, y: 0)に配置されていることがわかります。

この結果を見ると、HStackから提案された描画可能な空間起点で自身の座標を決めていることがわかります。

また、上記のコード内において、Image(systemName: “circle”) と Image(systemName: “star”) Viewの親View座標空間は、HStackから提案された描画可能な空間 を指していることがわかります。

まとめ

本記事ではSwiftUIの.position の挙動を確認しました。

.position モディファイアの動きへの理解を深める一助となれば幸いです。