Is there a way to disable TabItems in a TabView similar to .disabled() on Buttons in SwiftUI?

Viewed 77

I have a TabView with three TabItems. When I press a Button on the first TabItem, I then want to disable the user's ability to tap and go to TabItem two and three.

I've used disabled(), but I can still tap and go to them.

TabView(selection: $tabSelected) {
    MeditationTimer(vm: vm)
        .tabItem({
            Label("Meditate", systemImage: "timer")
        })
        .tag(0)
    
    History(vm: vm)
        .tabItem {
            Label("Sessions", systemImage: "list.bullet")
        }
        .tag(1)
        .disabled(vm.isActive)

    Settings(vm: vm)
        .tabItem {
            Label("Settings", systemImage: "gearshape")
        }
        .tag(2)
        .disabled(vm.isActive)
}

Putting .disabled() on TabView itself actually works, but yeah, then nothing can be tapped in my whole app anymore. Like the stop button on the first TabItem.

Any help is greatly appreciated!

3 Answers

Using selection and onChange of the TabView.

Try this code below, it may be what you needed:

struct ContentView: View {
@State var selection = "A"
@State var isDisable = false
var body: some View {
    TabView(selection: $selection) {
        VStack {
            Button {
                isDisable.toggle()
            } label: {
                Text("View A")
                Text("DISABLE B & C ? = \(String(isDisable))")
                    .foregroundColor(.red)
            }
        }
        .tabItem {
            Image(systemName: "note")
        }
        .tag("A")
        
        Text("View B")
            .tabItem {
                Image(systemName: "note")
            }
            .tag("B")
        
        Text("View C")
            .tabItem {
                Image(systemName: "note")
            }
            .tag("C")
    }
    .onChange(of: selection) { newValue in
        if isDisable {
            selection = "A"
        }
    }
  }
}

The only thing I can think of is selecting the desired tab when your desired condition is met. Something like this:

final class ContentViewModel: ObservableObject {
    @Published var selection = 0 {
        didSet {
            if oldValue == 0 && selection != 0 {
                selection = 0
            }
        }
    }
}

struct ContentView: View {
    @StateObject var viewModel = ContentViewModel()

    var body: some View {
        TabView(selection: $viewModel.selection) {
            View1()
                .tabItem {
                    Label("Meditate", systemImage: "timer")
                }
                .tag(0)
            
            View2()
                .tabItem {
                    Label("Sessions", systemImage: "list.bullet")
                }
                .tag(1)

            View3()
                .tabItem {
                    Label("Settings", systemImage: "gearshape")
                }
                .tag(3)
        }
    }
}

I think I actually found a potential solution. I think it will work for me. I put the conditional TabItem views into an if statement. When the condition is true, they disappear, leaving only a tab bar view the desired sinlge TabItem.

TabView(selection: $tabSelected) {
            MeditationTimer(vm: vm, isDisable: $isDisable)
                .tabItem({
                    Label("Meditate", systemImage: "timer")
                })
                .tag(0)
            if !vm.isActive {
                History(vm: vm)
                    .tabItem {
                        Label("Sessions", systemImage: "list.bullet")
                    }
                    .tag(1)
                    
                Settings(vm: vm)
                    .tabItem {
                        Label("Settings", systemImage: "gearshape")
                    }
                    .tag(2)
            }
        }
Related