BottomBarが消えるバグが存在する
SwiftUIは、BottomBarのアイテムが消える既知のバグが存在する。
発生条件は以下の通りである。
発生条件
- iOS14.4(執筆時の最新OS)
- ListのContentにNavigationLinkを指定している
- 「親階層→子階層→孫階層→子階層→親階層」の順に画面遷移
バグが発生してしまうコード
struct ContentView: View {
var body: some View {
NavigationView {
List {
NavigationLink(
destination: ChildView(),
label: {
Text("Parent")
})
}
.toolbar {
ToolbarItem(placement: .bottomBar) {
Text("Item")
}
}
.navigationTitle("バグ発生")
}
}
}
struct ChildView: View {
var body: some View {
NavigationLink(
destination: Text("Grandchild"),
label: {
Text("Child")
})
.toolbar {
ToolbarItem(placement: .bottomBar) {
Text("Item")
}
}
}
}
解決策1:idインスタンスメソッドを活用
StackOverflowに解決策として記載されていました。
idインスタンスメソッドとは?
プロキシ値が更新された場合、画面を再描画します。
idインスタンスメソッドを活用した解決策
struct ContentView: View {
@State var refresh = UUID() // ← 固有識別番号を割り当てる
var body: some View {
NavigationView {
List {
NavigationLink(
destination: ChildView(),
label: {
Text("Parent")
})
}
.toolbar {
ToolbarItem(placement: .bottomBar) {
Text("Item")
}
}
.navigationTitle("バグ発生")
.id(refresh) // ← 子と孫階層の再描画を防ぐ
}
}
}
解説
StackOverflowの解説によると、
描画タイミングを自明にすることで子階層の強制再描画を防ぐ
と書いていた。しかし、私の環境はバグを回避することができませんでした。一応、StackOverflowの有識者がバグの回避策として提言されていたため、執筆しました。
解決策2:ListのContentにNavigationLinkを宣言しない
バグの発生条件として、「ListのContentにNavigationLinkを指定している」が存在する。そのため、Content
にNavigationLink
を指定しなければバグが発生しない。
Contentに宣言せず、遷移処理を実装する
struct ContentView: View {
@State var transition = false
var body: some View {
NavigationView {
VStack {
NavigationLink(
destination: ChildView(),
isActive: $transition,
label: {})
List {
Button(action: {
transition.toggle()
}, label: {
Text("Parent")
})
}
}
.toolbar {
ToolbarItem(placement: .bottomBar) {
Text("Item")
}
}
.navigationTitle("バグ回避")
}
}
}
解説
NavigationLink
のisActive
を用いて遷移処理を実装します。label
パラメーターを宣言しない状態だと、画面にアイテムが描画しない状態でNavigationLink
を宣言することができます。そしてVStack(or HStack)
でButton
とセットにしなければエラーの原因になるので、注意が必要です。
注意
この解決策はガイドラインに則っていません。また、コードも汚いです。
最後に
今回はBottomBarが消えるバグの回避策を執筆しました。ガイドラインに適さない手法で実装しており、褒められた手段ではありません。
他の手段もしくは記事に間違いがある場合、コメント欄で指導してくださると幸いです。