Home > Software engineering >  How do I prevent the user from switching to certain tabs in a SwiftUI TabView
How do I prevent the user from switching to certain tabs in a SwiftUI TabView

Time:02-28

I am creating sort of a game in swift that gives the user a new clue every time they guess incorrectly. I would like to have a TabView for the clues, but I don’t want the player to have access to every clue at the start. I was thinking either locking the tabs in the TabView until a function is called or making an array of different views in the TabView that can be edited.

CodePudding user response:

One note before starting my reply: in this community you need to be clear about the issue and show examples of your code, error messages, points where you get stuck. Your answer is quite generic, but I will give you some hints.

Each Tab is a view (almost) like any other one, it can be hidden conditionally.

You can just hide the tabs until the user reaches the point in the game where the clues must be shown. In addition, or in alternative, you can switch to different views to see each clue.

See an example below (there's room for improvement, this is the idea):

struct Tabs: View {
    
    @State private var showTab2 = false      // This will trigger the tab to be shown
                                             // It can be stored in your view model
    
    var body: some View {
        TabView {
            Example(showNextClue: $showTab2)
                .tabItem {Label("Tab 1", systemImage: "checkmark")}
            
            if showTab2 {         // Tab 2 is hidden until you change the state variable
                Tab2()
                    .tabItem {Label("Tab 2", systemImage: "flag")}
            }
            
            Example(showNextClue: $showTab2)
                .tabItem {Label("Tab 3", systemImage: "trash")}
        }
    }
}

struct Tab2: View {
    
    @State private var clueIndex = 0     // What clue will you show now?
    
    var body: some View {
        VStack {
            switch clueIndex {           // Show the applicable clue
                                         // You can iterate through an array of views as you proposed, chose the best method
            case 0:
                Text("Now you can see this clue number 1")
            case 1:
                Text("Clue number 2")
            default:
                Text("Clue \(String(clueIndex))")
            }
            
            Button {
                clueIndex  = 1
            } label: {
                Text("Show next clue")
            }
            .padding()
        }
    }
}

struct Example: View {
    
    @Binding var showNextClue: Bool
    
    var body: some View {
            VStack {
                Text("This is open")
                    .padding()
                
                Button {
                    showNextClue.toggle()
                } label: {
                    Text(showNextClue ? "Now you can see the next clue" : "Click here to see your next clue")
                }
            }
    }
}

CodePudding user response:

Unfortunately you cannot disable the standard TabBar selectors. But you can do your own custom ones, e.g. like this:

struct ContentView: View {
    
    @State private var currentTab = "Home"
    @State private var activeTabs: Set<String> = ["Home"] // saves the activated tabs

    var body: some View {
        VStack {
            TabView(selection: $currentTab) {
                // Tab Home
                VStack(spacing: 20) {
                    Text("Home Tab")
                    Button("Unlock Hint 1") { activeTabs.insert("Hint 1")}
                    Button("Unlock Hint 2") { activeTabs.insert("Hint 2")}
                }
                .tag("Home")
                
                // Tab 1. Hint
                VStack {
                    Text("Your first Hint")
                }
                .tag("Hint 1")

                // Tab 2. Hint
                VStack {
                    Text("Your Second Hint")
                }
                .tag("Hint 2")
            }
            // custom Tabbar buttons
            Divider()
            HStack {
                OwnTabBarButton("Home", imageName: "house.fill")
                OwnTabBarButton("Hint 1", imageName: "1.circle")
                OwnTabBarButton("Hint 2", imageName: "2.circle")
            }
        }
    }
    
    func OwnTabBarButton(_ label: String, imageName: String) -> some View {
            Button {
                currentTab = label
            } label: {
                VStack {
                    Image(systemName: imageName)
                    Text(label)
                }
            }
            .disabled(!activeTabs.contains(label))
            .padding([.horizontal,.top])
    }
}
  • Related