Jeff Cournoyer
Jeff Cournoyer

Reputation: 504

SwiftUI - Detect which TabView is active onAppear

I have a TabView that is showing 4 different views. The whole tabView is set to:

.tabViewStyle(PageTabViewStyle())

(So I can make it Swipe-enabled instead of using the buttons)

I also have an HStack of simple rectangles that should highlight different colors when their respective pages are displayed. (The first rectangle will be showed as blue when the first tab is shown, the second rectangle is blue when the second page is active, etc.) I'm doing this with an onAppear on each of the views.

ViewOne()
   .onAppear {
            print("1")
            activeTab = 1
        }

If I only use two views in the tabView, this works fine. But as soon as I add the third, the onAppear goes crazy. When I swipe to the third view the print statement adds to the console in quick succession:

3
1
4

And the blue rectangle is the 4th one. The 4th page throws it off even more and things just gets strange.

My question is this: are the tabViews sort of like the dequeResuableCell of a UIKit TableView, or am I doing something wrong? Might it be a bug? and if so, is there a workaround for this that doesn't use onAppear?

EDIT: Here's the full code implementation:

        VStack {
        
        HStack {
            
            Rectangle()
                .foregroundColor((activeTab == 1) ? .blue : .red)
            
            Rectangle()
                .foregroundColor((activeTab == 2) ? .blue : .red)
            
            Rectangle()
                .foregroundColor((activeTab == 3) ? .blue : .red)
            
            Rectangle()
                .foregroundColor((activeTab == 4) ? .blue : .red)
            
        }
        .frame(height: 100)
        .padding()
        
        TabView(selection: $activeTab) {
// each one of the Views below has the .onAppear attached to them
               
            viewTwo(activeTab: $activeTab)
            
            viewTwo(activeTab: $activeTab)
            
            viewThree(activeTab: $activeTab)
            
            viewFour(activeTab: $activeTab)
            
        }
        .tabViewStyle(PageTabViewStyle())
    }
}

Upvotes: 2

Views: 3233

Answers (1)

Jeff Cournoyer
Jeff Cournoyer

Reputation: 504

So it turns out I was overcomplicating this A LOT! Rather than attempting to put an .onAppear on each of the associated views (which I've come to realize is not always perfectly reliable), I just used:

.onChange(of: activeTab, perform: { value in
            print(activeTab)
})

This stabilized the code and performs the way I wanted it to.

My full code is below if it might be able to help anyone!

    struct EvalView: View {
    
    @State var activeTab = 1
    
    var body: some View {
        VStack {
            
            HStack {
                
                Rectangle()
                    .foregroundColor((activeTab == 1) ? .blue : .red)
                
                Rectangle()
                    .foregroundColor((activeTab == 2) ? .blue : .red)
                
                Rectangle()
                    .foregroundColor((activeTab == 3) ? .blue : .red)
                
                Rectangle()
                    .foregroundColor((activeTab == 4) ? .blue : .red)
                
            }
            .frame(height: 100)
            .padding()
            
            TabView(selection: $activeTab) {
                   
                viewOne(activeTab: $activeTab)
                    .tag(1)
                
                viewTwo(activeTab: $activeTab)
                    .tag(2)
                
                viewThree(activeTab: $activeTab)
                    .tag(3)
                
                viewFour(activeTab: $activeTab)
                    .tag(4)
                
            }
            .tabViewStyle(PageTabViewStyle())
            .onChange(of: activeTab, perform: { value in
                print(activeTab)
            })
        }
    }
}

struct viewOne: View {
    
    @Binding var activeTab: Int
    
    var body: some View {
        Text("1")
    }
}

struct viewTwo: View {
    
    @Binding var activeTab: Int
    
    var body: some View {
        Text("2")
    }
}


struct viewThree: View {
    
    @Binding var activeTab: Int
    
    var body: some View {
        Text("3")
    }
}

struct viewFour: View {
    
    @Binding var activeTab: Int
    
    var body: some View {
        Text("4")
    }
}

    struct EvalView_Previews: PreviewProvider {
        static var previews: some View {
            EvalView()
        }
    }

Upvotes: 4

Related Questions