1
votes

I have a tabview of more then 5 tabs, in SwiftUI It automtically creates a more tab to show the extra tabs. This has been working fine. However I wanted to set the first tab the user saw when the app opened so I added an EnviromentObject which stores the selected tab. This allows me to set the first tab the user sees and change tab programitcally. For example on one of the tabs the user uploads an image which takes a while to process, so when the image is uploaded the function changes the selected tab which then chages the users view.

This all works great, however the more tab now no longer works, it trys to show the more page but then flicks back to tab selection which is in the EnviromentObject.

How can I stop this behaviour?

My tabView:


    struct BasicView: View {
        @EnvironmentObject var screenCoordinator: ScreenCoordinator
        @State var climbs:ClimbListData?
        @State var selection = 2
        var body: some View {
            TabView(selection: $screenCoordinator.selection) {
                NavigationView{
                    VStack{
                        Text("Search")
                    }.navigationBarTitle("Climbers Log", displayMode:.inline)
                }.tabItem{
                    Image(systemName:"magnifyingglass")
                    Text("Search")
                }.tag(0)
                NavigationView{
                    VStack{
                        MapView()
                    }.navigationBarTitle("Climbers Log", displayMode:.inline)
                }.tabItem{
                    Image(systemName: "map.fill")
                    Text("Map")
                }.tag(1)
                
                NavigationView{
                    VStack{
                        ClimbList()
                    }.navigationBarTitle("Climbers Log", displayMode:.inline)
    
                }.tabItem{
                    ScalledImage(name: "climbericon", size: CGSize(width: 24, height: 24))
                    Text("Climbs")
                }.tag(2)
                NavigationView{
                    VStack{
                        AddAClimb()
                    }
                }.tabItem{
                    Image(systemName:"plus")
                    Text("Add Climb")
                }.tag(6)
                NavigationView{
                    VStack{
                        Text("Log")
                    }.navigationBarTitle("Climbers Log", displayMode:.inline)
                }.tabItem{
                    Image(systemName:"book.fill")
                    Text("Log")
                }.tag(3)
                NavigationView{
                    VStack{
                        Text("Profile")
                    }.navigationBarTitle("Climbers Log", displayMode:.inline)
                }.tabItem{
                    Image(systemName:"person.circle")
                    Text("Profile")
                }.tag(4)
                NavigationView{
                    VStack{
                        Text("Settings")
                    }.navigationBarTitle("Climbers Log", displayMode:.inline)
                }.tabItem{
                    Image(systemName:"gear")
                    Text("Settings")
                }.tag(5)
                
                NavigationView{
                    VStack{
                        Text("Stats")
                    }.navigationBarTitle("Climbers Log", displayMode:.inline)
                }.tabItem{
                    ScalledImage(name: "stats", size: CGSize(width: 24, height: 24))
                    Text("Stats")
                }.tag(7)
            }.edgesIgnoringSafeArea(.top).navigationTitle("").navigationBarHidden(true)
        }
        
    } 

my EnviromentObject:


    class ScreenCoordinator: ObservableObject {
        @Published var selection = 2 
    }

1

1 Answers

1
votes

This looks like a bug of TabView+ObservedObject.

Here is possible workaround - use internal state and synchronise it with value of environment object. Tested with Xcode 12 / iOS 14.

struct BasicView: View {
        @EnvironmentObject var screenCoordinator: ScreenCoordinator
        @State var climbs:ClimbListData?

        @State private var selection = 0    // << use state !!

        var body: some View {
            TabView(selection: $selection) {    // << local selection

               // ... your tabs here
                     
             }.edgesIgnoringSafeArea(.top)
             .navigationTitle("").navigationBarHidden(true)
             .onAppear {
                // initially loaded
                self.selection = self.screenCoordinator.selection
            }
            .onChange(of: selection) {
                // store local to environment
                self.screenCoordinator.selection = $0
            }
            .onReceive(screenCoordinator.$selection) {
                // update state on env changed
                self.selection = $0
            }
        }

    }