🔭

iOS16 ignoresSafeArea 観測隊

2023/03/30に公開

環境

macOS Monterey 12.6.4
Xcode 14.2
iOS 16.2
シミュレータ iPhone 14 にて観測

はじめに

この記事ではSwiftUIの .ignoresSafeArea() の挙動を調べる。

注意

SafeAreaの定義を、 コンテンツが隠れずに表示できる部分 、とする。端のエリアではなく中のエリアとする。
SafeAreaより外の部分をDangerAreaとする。
表など短い表記を用いる場面で、SafeAreaに収まることをS、DangerAreaまで拡張することをDと書く。
ViewにignoresSafeAreaを付けないことをデフォルトという。
.ignoresSafeArea()を短く表記するときは.iSA()と書く。
各ソースはbodyの直下に書かれている。

Rectangle

var body: some View {
    Rectangle()
        .foregroundColor(.red)
        //.ignoresSafeArea()
}

デフォルト .iSA()
Rectangle S D

それぞれの意味は
デフォルトは「 .ignoresSafeArea() を付けない」
.iSA()は「 .ignoresSafeArea() を付ける」
Sは「SafeAreaに収まる」
Dは「DangerAreaまで拡張する」

Image

画像は灰色のpngファイルを使用。

var body: some View {
    Image("Rectangle")
        .resizable()
	//.ignoresSafeArea()
}

Image("xx")
.resizable()
Image("xx")
.resizable()
.iSA()
Image S D

.resizable() を付けないときはその画像本来の大きさになる。今回の調査では.resizable()付きで調査。

VStack - Rectangle

var body: some View {
    VStack {
        Rectangle()
	    .foregroundColor(.red)
            //.ignoresSafeArea()
    }
    .background(.green)
    //.ignoresSafeArea()
}

デフォルト VStack
のみに
.iSA()
Rectangle
のみに
.iSA()
両方に
.iSA()
VStack D D D D
Rectangle S D D D

VStackの背景色を付けるとDangerAreaまで拡張していることがわかる。
デフォルトでは中に置いたRectangleはSafeAreaに収まる。
どちらかに ignoresSafeArea() を付けるとRectangleはDangerAreaまで拡張する。

VStack - (Rectangle, Rectangle)

VStack {
    Rectangle()
        .foregroundColor(.red)
        //.ignoresSafeArea()
    Rectangle()
        .foregroundColor(.yellow)
        //.ignoresSafeArea()
}
.background(.green)
//.ignoresSafeArea()

デフォルト VStack
のみに
.iSA()

Rectangle
のみに
.iSA()

Rectangle
のみに
.iSA()
VStack D D D D
上Rectangle S D D S
下Rectangle S D S D
Rectangleの境目 基準値 わかりずらいが
少し上に変化
基準値と同じ 基準値と同じ

考察

以下の仮説は上の現象とつじつまが合う。
VStackは中にコンテンツ領域を持つ。
VStackにignoresSafeAreaするとコンテンツ領域が広がる。上のDangerAreaと下のDangerAreaは幅が違うので広がる部分は上と下のアンバランスになり、(領域を2分割された)Rectangleの境目が微妙に変わる。
RectangleのみにignoresSafeAreaすると、VStackの中のコンテンツ領域は変わらず、コンテンツ領域を2分配された領域も変わらず、ignoresSafeAreaの適用部分だけ器用に拡張される

VStack - (Rectangle, Text)

VStack {
    Rectangle()
        .foregroundColor(.red)
        //.ignoresSafeArea()
    Text("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
        //.ignoresSafeArea()
}
.background(.green)
//.ignoresSafeArea()

デフォルト VStack
のみに
.iSA()
Rectangle
のみに
.iSA()
Text
のみに
.iSA()
VStack D D D D
Rectangle S D D S
Text S D S S

考察

外側のVStackがデフォルトのとき、中にあるRectangleにignoresSafeAreaをすると拡張するが、TextにignoresSafeAreaをしても拡張しないようである。(ただし緑の領域は少し変化?)。外側のVStackが作ったコンテンツ領域を破壊して拡張できるかどうはViewの性質によって異なるのかもしれない。

VStack - (Text, Rectangle)

先ほどとRectangleとTextの順番を入れ替えただけである。が、しかし。。。

VStack {
    Text("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
        //.ignoresSafeArea()
    Rectangle()
        .foregroundColor(.red)
        //.ignoresSafeArea()
}
.background(.green)
//.ignoresSafeArea()

デフォルト VStack
のみに
.iSA()
Text
のみに
.iSA()
Rectangle
のみに
.iSA()
VStack D D D D
Text S D D S
Rectangle S D S D

今度はTextのみにignoresSafeAreaしたときに拡張された。
Textは上にあるときと下にあるときで挙動が違う。
上にあるときはVStackが作ったコンテンツ領域を破壊する
下にあるときはVStackが作ったコンテンツ領域を破壊しない

ただし、Textが上にあってDangerAreaまで拡張しても、Rectangleの領域はTextが拡張しないで元の位置に来てもいいようなおもてなし(?)で描かれている。

考察

意図していない挙動だと思われる。
TextをButtonやTextFieldに変えても同様の挙動。

ScrollView - VStack - (Text)

ScrollView {
    VStack {
        Text("AAAAAAAAA")
            .font(Font.system(size: 50))
            //.ignoresSafeArea()
        Text("A")
            .font(Font.system(size: 200))
            //.ignoresSafeArea()
        Text("A")
            .font(Font.system(size: 200))
            //.ignoresSafeArea()
        Text("A")
            .font(Font.system(size: 200))
            //.ignoresSafeArea()
        Text("A")
            .font(Font.system(size: 200))
            //.ignoresSafeArea()
        Text("AAAAAAAAA")
            .font(Font.system(size: 50))
            //.ignoresSafeArea()
    }
    .background(.green)
    //.ignoresSafeArea()
}
.background(.purple)
//.ignoresSafeArea()

ここではScrollViewの中身を「中身」と表現する。

デフォルト ScrollView

.iSA()
ScrollView D D
VStack S D
中身 S D

中身がたくさんあるので ignoresSafeArea() を付ける組み合わせはいろいろあるのだが、実際は次の2パターン

  • ScrollViewに ignoresSafeArea() を付けない。この時中身はSafeArea内に表示され、他をどう指定しても変わらない。
  • ScrollViewに ignoresSafeArea() を付ける。この時すべてはDangerAreaに拡張され、他をどう指定しても変わらない。

コンテンツがSafeArea内に表示されていても、スクロールに従い外に外れるとき(or 外から入ってくるとき)はDangerAreaにも表示する。スクロールを一番上にする(先頭を表示させようとする)と先頭のものはSafeArea内に表示し、一番下にすると最後のものはSafeArea内に表示する。

個々のコンテンツに ignoresSafeArea() を付けると、間隔に(バグのようにも感じられる)ランダムな影響がある。仕組みは理解できなかった。

あとがき

他にもパターンがあると思うがこのへんで。

Discussion