SwiftUI onAppear called when TabView is released

848 Views Asked by At

I have a TabView with 4 tabs (page1, page2, page3, menu). When I click on any tab, onAppear is called, the problem is when I select menu and then click logout to switch from TabView to Login screen, all pages loaded before are called again (onAppear fired again)

I've reproduced with this little example. This is problem for me, cause I have many views where I need to call network service, so I expect that onAppear is called only when switching between tabs. Any solution?

struct ContentView: View {
    @State var isLogout = false
    var body: some View {
        if isLogout {
            Text("Login")
        } else {
            TabView {
                Text("Page1")
                    .onAppear{
                        print("Page1 Appeared")
                    }
                    .tabItem {
                        Label("Page1", systemImage: "square")
                    }
                Text("Page2")
                    .onAppear{
                        print("Page2 Appeared")
                    }
                    .tabItem {
                        Label("Page2", systemImage: "circle")
                    }
                VStack {
                    Text("Menu")
                    Button("Logout") {
                        isLogout = true
                    }
                }
                .onAppear{
                    print("Menu Appeared")
                }
                .tabItem {
                    Label("Menu", systemImage: "list.dash")
                }
            }
        }
    }
}

Logs:

Page1 Appeared
Page2 Appeared
Menu Appeared
Login Appeared
Page2 Appeared
Page1 Appeared
2

There are 2 best solutions below

2
Nhat Nguyen Duc On

I don't know why the problem happened. But you can detect when the user switches between tabs by using a variable to store the current selection:

@State private var currentPage = 0

Uses:

TabView(selection: $currentPage) {
    ...
}

Use onChanged to detect when the user switches tabs:

.onChange(of: currentPage) { page in
    switch page {
    case 0: print("Page1")
    case 1: print("Page2")
    default: print("Page1")
    }
}

And don't forget to give each view a tag:

Text("Page 1")
    .tag(0)
    .tabItem {
        Label("Page 1", systemImage: "app")
    }
Text("Page 2")
    .tag(1)
    .tabItem {
        Label("Page 2", systemImage: "circle")
    }
0
vacawama On

I have confirmed your example. One possible workaround is to check for isLogout inside of .onAppear. If your tab views are separate views, pass isLogout in as a @Binding

TabView {
    Text("Page1")
        .onAppear{
            if !isLogout {
                print("Page1 Appeared")
            }
        }
        .tabItem {
            Label("Page1", systemImage: "square")
        }