I have the following code snippet. The 'selectedTab' in 'FirstTabView' updates automatically when I swipe between tabs. But if I change 'selectedTab' to be an optional value as shown in 'SecondTabView', then the 'selectedTab' does not update and always show 2. Could someone help explain it?
struct FirstTabView: View {
@State private var selectedTab: Int = 2
var body: some View {
VStack {
TabView(selection: $selectedTab) {
Text("Tab 1")
.tag(1)
Text("Tab 2")
.tag(2)
Text("Tab 3")
.tag(3)
}
.tabViewStyle(PageTabViewStyle())
Text(String(selectedTab))
}
}
}
struct SecondTabView: View {
@State private var selectedTab: Int?
var body: some View {
VStack {
TabView(selection: $selectedTab) {
Text("Tab 1")
.tag(1)
Text("Tab 2")
.tag(2)
Text("Tab 3")
.tag(3)
}
.tabViewStyle(PageTabViewStyle())
Text(String(selectedTab ?? 2))
}
}
}
I create a new non-optional variable to bind the selection and then the 'selectedTab' can update automatically again. But I checked the SwiftUI Documentation and the tabview does accept an optional value as selection parameter.
struct SecondTabView: View {
@State private var selectedTab: Int?
var tabBinding: Binding<Int> {
Binding(
get: { selectedTab ?? 2 },
set: { selectedTab = $0 }
)
}
var body: some View {
VStack {
TabView(selection: $tabBinding) {
Text("Tab 1")
.tag(1)
Text("Tab 2")
.tag(2)
Text("Tab 3")
.tag(3)
}
.tabViewStyle(PageTabViewStyle())
Text(String(selectedTab ?? 2))
}
}
}
The type of the selection must exactly match the type of the
tag
s you give to the tabs.You are giving
Int
tags to the tabs, but the selection's type isInt?
, so the selection value is not updated. There is noInt?
value SwiftUI can set, such that it is equal to (by using the==
operator required byEquatable
) the non-nullableInt
"1". After all,Equatable
can only compare things of the same type.Note that although you can normally compare
Int
andInt?
because as a language feature, you are allowed to write the non-optional value where an optional is expected. The internals of SwiftUI doesn't have this "feature".If you want to use an optional type as the selection value, make the tags optional too:
That said, the initial value of
selectedTab
would benil
, and there is no tabs with the tagnil
, so (at least in my testing) the first tab is initially selected (but does not change the selection value to 1).So I'm not sure why you would use an optional selection value in this case. In fact, I can't think of any case where it would be useful. If you want to have a "default" tab, just initialise
selectedTab
to one of the tags.You have probably found this:
Binding<SelectionValue>?
is not a binding of an optional value, which would be writtenBinding<SelectionValue?>
. It is a binding that is optional, i.e. it allows you to write:So when you pass your
$selectedTab
, which is aBinding<Int?>
, the tab view'sSelectionValue
isInt?
, and it looks for tags of typeInt?
.