iOS16 ignoresSafeArea 観測隊
環境
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