0

I'm having trouble with what I think may be a bug, but most likely me doing something wrong.

I have a slightly complex navigation state variable in my model that I'm using for tracking/setting state between tab and sidebar presentations when multitasking on iPad. That all works fine except in tab mode, once I use a navigation link once I can't seem to use one again, whether the binding is on my tab view or navigation links in a list.

enter image description here

Would really appreciate any thoughts on this, Cheers!

Example

NavigationItem.swift

enum SubNavigationItem: Hashable {
    case overview, user, hobby
}

enum NavigationItem: Hashable {
    case home(SubNavigationItem)
    case settings
}

Model.swift

final class Model: ObservableObject {
    @Published var selectedTab: NavigationItem = .home(.overview)
}

SwiftUIApp.swift

@main
struct SwiftUIApp: App {
    @StateObject var model = Model()
    
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(model)
        }
    }
}

ContentView.swift

struct ContentView: View {
    var body: some View {
        AppTabNavigation()
    }
}

AppTabNavigation.swift

struct AppTabNavigation: View {
    @EnvironmentObject private var model: Model

    var body: some View {
        TabView(selection: $model.selectedTab) {
            NavigationView {
                HomeView()
            }
            .tabItem {
                Label("Home", systemImage: "house")
            }
            .tag(NavigationItem.home(.overview))

            NavigationView {
                Text("Settings View")
            }
            .tabItem {
                Label("Settings", systemImage: "gear")
            }
            .tag(NavigationItem.settings)
        }
    }
}

HomeView.swift

I created a binding here because selection required an optional <NavigationItem?> not

struct HomeView: View {
    @EnvironmentObject private var model: Model
    
    var body: some View {
        let binding = Binding<NavigationItem?>(
            get: { 
                model.selectedTab 
            },
            set: {
                guard let item = $0 else { return }
                model.selectedTab = item
            }
        )
        
        List {
            NavigationLink(
                destination: Text("Users"),
                tag: .home(.user),
                selection: binding
            ) {
                Text("Users")
            }
            NavigationLink(
                destination: Text("Hobbies"),
                tag: .home(.hobby),
                selection: binding
            ) {
                Text("Hobbies")
            }
        }
        .navigationTitle("Home")
    }
}

Second Attempt

I tried making the selectedTab property optional as @Lorem Ipsum suggested. Which means I can remove the binding there. But then the TabView doesn't work with the property. So I create a binding for that and have the same issue but with the tab bar!

enter image description here

Ryan
  • 145
  • 1
  • 6

1 Answers1

2

Make the selected tab optional

@Published var selectedTab: NavigationItem? = .home(.overview)

And get rid of that makeshift binding variable. Just use the variable

$model.selectedTab

If the variable can never be nil then something is always selected IAW with that makeshift variable it will just keep the last value.

lorem ipsum
  • 21,175
  • 5
  • 24
  • 48
  • Thanks for the reply! I can do this, but then the TabView doesn't work, so then I create a similar binding and then my TabView items have the same issue. *I have updated the post with what I tried here – Ryan Jul 22 '21 at 09:21
  • You can’t mix both like that and expect different behaviors. If you want the tabview to work you get the previous behavior if you want the link to deselect you need this behavior. – lorem ipsum Jul 22 '21 at 09:35
  • 1
    You might get close if you have a button or an onChange activate your navigation link instead – lorem ipsum Jul 22 '21 at 09:53
  • Yeah I understand what you suggested definitely works, thanks for explaining. It's a bit unfortunate because for e.g. using a TabView and Sidebar(navigation links) . I need to keep the navigation item in sync, which is not nice if I can't use the same. Which is why I tried one property and binding where needed. Sorry to ask further, but do you have a suggestion for that? – Ryan Jul 22 '21 at 09:56