😊
【SwiftUI】スクロール可能なViewに対してはoverlayではなく、safeAreaInsetでボタン配置を行うといい
overlayではなくsafeAreaInsetを用いる理由
ScrollView,List,Formなどを使っている時、右下にボタンを配置したいということはよくある話だと思います。(右下にボタンを配置するサンプルコードは最後にあります。)
そんな時、overlay
を使えば、右下にボタンを実装することができますが、コンテンツとボタンが被ってしまうという問題があります。(最下部までスクロールしても最後の行(No. 19)が表示されない。)
struct ContentView: View {
var body: some View {
List(0..<20) { num in
Text("No. \(num)")
}
.overlay(alignment: .bottom) {
Text("Hello, world.")
.foregroundStyle(.white)
.font(.largeTitle)
.frame(maxWidth: .infinity)
.padding()
.background(.blue)
}
}
}
これを解決してくれるのがsafeAreaInset
です。overlayは言うなれば上塗りですが、safeAreaInsetは差し込みなので、最下部の行(No. 19)と重なってしまうことはありません。
※ iOS15.2以降でScrollViewだけでなく、List, Formも対応しました。
struct ContentView: View {
var body: some View {
List(0..<20) { num in
Text("No. \(num)")
}
.safeAreaInset(edge: .bottom) {
Text("Hello, world.")
.foregroundStyle(.white)
.font(.largeTitle)
.frame(maxWidth: .infinity)
.padding()
.background(.blue)
}
}
}
サンプルコード
右下にボタンを配置したい場合は、以下の通りです。
struct ContentView: View {
var body: some View {
List(0..<20) { num in
Text("No. \(num)")
}
.safeAreaInset(edge: .bottom, alignment: .trailing) {
Button("action", action: {})
.buttonStyle(.borderedProminent)
.buttonBorderShape(.capsule)
.padding(.trailing)
}
}
}
複数のボタンを配置することもできます。
struct ContentView: View {
var body: some View {
List(0..<20) { num in
Text("No. \(num)")
}
.safeAreaInset(edge: .bottom, alignment: .trailing) {
Button("action1", action: {})
.tint(.orange)
.buttonStyle(.borderedProminent)
.buttonBorderShape(.capsule)
.padding(.trailing)
}
.safeAreaInset(edge: .bottom, alignment: .trailing) {
Button("action2", action: {})
.tint(.green)
.buttonStyle(.borderedProminent)
.buttonBorderShape(.capsule)
.padding(.trailing)
}
}
}
公式ドキュメント
Discussion