Search code examples
swiftuitabbar

SwiftUI Tab Selection Not Working With Any Hashable Content


SwiftUI’s Tab selection is suppose to work with any hashable content however that doesn’t seem to work.

In the example provided, you can see that in “Working” Tab, eveything works correctly if you use an integer for the tab selection. When you switch over to the “Broken” tab, the selection is a ColorItem and the selection does not update the view.

I believe this is a SwiftUI bug and have filed a feedback(FB8879981).

Tested with Xcode 12.2 and iOS 14.2(RC).

struct ColorItem: Identifiable, Hashable{
    let color: Color
    let title: String
    
    var id: String{
        title
    }
}
struct ContentView: View {
    let items = [
        ColorItem(color: .red, title: "Red"),
        ColorItem(color: .blue, title: "Blue"),
        ColorItem(color: .purple, title: "Purple")
    ]
    
    var body: some View {
        TabView{
            TabViewWorking(items: items)
                .tabItem {
                    Label("Working", systemImage: "hand.thumbsup")
                }
            
            TabViewBroken(items: items)
                .tabItem {
                    Label("Broken", systemImage: "hand.thumbsdown")
                }
        }
    }
}
struct TabViewWorking: View {
    @State private var tabSelection = 0
    let items: [ColorItem]
    
    var body: some View {
        ZStack{
            TabView(selection: $tabSelection){
                ForEach(0..<items.count){ i in
                    items[i].color.edgesIgnoringSafeArea(.all)
                        .tag(i)
                }
            }
            .tabViewStyle(PageTabViewStyle())
            
            VStack{
                Text(tabSelection.description)
                Text(items[tabSelection].title)
            }
            .font(.largeTitle)
        }
    }
}
struct TabViewBroken: View {
    @State private var tabSelection = ColorItem(color: .red, title: "Red")
    let items: [ColorItem]

    var body: some View {
        ZStack{
            TabView(selection: $tabSelection){
                ForEach(0..<items.count){ i in
                    items[i].color.edgesIgnoringSafeArea(.all)
                        .tag(i)
                }
            }
            .tabViewStyle(PageTabViewStyle())
            
            VStack{
                Text(items.firstIndex(of: tabSelection)?.description ?? "N/A")
                Text(tabSelection.title)
            }
            .font(.largeTitle)
        }
    }
}

Solution

  • No, it is not SwiftUI bug. Type of selection and type of tag must be same, so in your first scenario they are both integers, but in second one they are not same - selection is ColorItem, but tag is still integer - thus selection does not work.

    Here is fixed variant:

    struct TabViewBroken: View {
        @State private var tabSelection = ColorItem(color: .red, title: "Red")
        let items: [ColorItem]
    
        var body: some View {
            ZStack{
                TabView(selection: $tabSelection){
                    ForEach(0..<items.count){ i in
                        items[i].color.edgesIgnoringSafeArea(.all)
                            .tag(items[i])                          // << here !!     
                    }
                }
                .tabViewStyle(PageTabViewStyle())
                
                VStack{
                    Text(items.firstIndex(of: tabSelection)?.description ?? "N/A")
                    Text(tabSelection.title)
                }
                .font(.largeTitle)
            }
        }
    }