🐥
[SwiftUI]NavigationViewとTabViewの同時使用について
SwiftUI
でNavigationView
とTabView
を同時に使いたかったので、挙動を調べてみました。
結論としてはNavigationViewの中にTabViewを置かない方が無難、でした。
環境
Xcode : 12.5.1
iOS14.5
試した方法
- ❌ NavigaitonView -> TabView -> NavigaitonViewとネストした方法
- 🔺 NavigationView -> TabView
- ○ TabView -> NavigationView
- ○ NavigationViewを使わない方法
NavigationView -> TabView -> NavigationView
NavigationViewの中にTabViewがあり、さらにその中にNavigationViewがある場合です。
struct ContentView: View {
@State var selection = 0
var body: some View {
NavigationView {
TabView(selection: $selection) {
FirstView()
.tabItem { Label("First", systemImage: "cloud.fill") }
.tag(0)
SecondView()
.tabItem { Label("Second", systemImage: "moon.fill") }
.tag(1)
}
}
}
}
struct FirstView: View {
var body: some View {
NavigationView {
VStack {
Text("Hello, First!")
NavigationLink.init(
destination: DetailView(),
label: { Text("Navigation") })
}.navigationBarTitle(Text("First"), displayMode: .inline)
}
}
}
結果
NavigationViewの高さが変になってしまいました。navigationBarHidden
などを指定しても変わりません。
NavigationView -> TabView
struct ContentView: View {
@State var selection = 0
var body: some View {
NavigationView {
TabView(selection: $selection) {
FirstView()
.tabItem { Label("First", systemImage: "cloud.fill") }
.tag(0)
SecondView()
.tabItem { Label("Second", systemImage: "moon.fill") }
.tag(1)
}
}
}
}
struct FirstView: View {
var body: some View {
VStack {
Text("Hello, First!")
NavigationLink.init(
destination: DetailView(),
label: { Text("Navigation") })
}
}
}
struct DetailView: View {
var body: some View {
VStack {
Text("Hello, Detail!")
}.navigationBarTitle(Text("Detail"), displayMode: .inline)
}
}
結果
一見、良さそうですが何やらエラーも表示されました。
Trying to pop to a missing destination at /Library/Caches/com.apple.xbs/Sources/Monoceros/Monoceros-103/Shared/NavigationBridge_PhoneTV.swift:337
ここら辺に原因があります。
TabView -> NavigationView
TabView
の中にNavigationView
がある場合です。
struct FirstView: View {
@Binding var showDetail: Bool
@Binding var name: String
var body: some View {
NavigationView {
VStack {
Text("Hello, First!")
Button("next") {
self.name = "First"
self.showDetail = true
}
NavigationLink.init("Navigation", destination: DetailView.init(name: .constant("Navigation")))
}
}
}
}
結果
動作には問題がなさそうです。タブを消したい場合は1つ前の方法が良いかもしれません。
NavigationViewを使わない方法
NavigationView
とTabView
は同時に使わず、sheet
等で画面を分ける方法を使用します。
struct ContentView: View {
@State var selection = 0
@State var showDetail = false
@State var name: String = ""
var body: some View {
TabView(selection: $selection) {
FirstView(showDetail: self.$showDetail, name: self.$name)
.tabItem { Label("First", systemImage: "cloud.fill") }
.tag(1)
SecondView(showDetail: self.$showDetail, name: self.$name)
.tabItem { Label("Second", systemImage: "moon.fill") }
.tag(2)
}.sheet(isPresented: self.$showDetail) {
DetailView(name: self.$name)
}
}
}
struct FirstView: View {
@Binding var showDetail: Bool
@Binding var name: String
var body: some View {
VStack {
Text("Hello, First!")
Button("next") {
self.name = "First"
self.showDetail = true
}
}
}
}
struct DetailView: View {
@Binding var name: String
var body: some View {
VStack {
Text("Hello, Detail!")
Text("from \(name)")
}
}
}
結果
結論
NavigationView
とTabView
を同時に使うと予期しない挙動になりそうなので、同時に使わない方が無難かもしれません。
Discussion