1

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
OuSS
  • 19
  • 3
  • Have you tested it on a real device? The problem didn't appear when I use my iPhone 12 running iOS 16.1 beta 5 – Nhat Nguyen Duc Oct 15 '22 at 14:25
  • onAppear is called again because you remove the views from the view hierarchy with your it statement. The answer that was already given (and now deleted) seemed like a good foundation for a solution. – jnpdx Oct 15 '22 at 15:38
  • I tested with iOS 16.1 beta 5 in real device but still same problem @NhatNguyenDuc – OuSS Oct 15 '22 at 16:42
  • It should call onDisappear, i guess its apple bug because onAppear have no sense @jnpdx – OuSS Oct 15 '22 at 16:45
  • I'm facing the same issue, did you happen to find the cause of the problem, or a possible fix? – Tünde Mar 07 '23 at 18:06
  • Have the same issue... This whole time I though I was doing something wrong in my KeychainStorage wrapper logic... – kironet May 30 '23 at 05:47

2 Answers2

0

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")
    }
Nhat Nguyen Duc
  • 432
  • 2
  • 10
  • It’s not what I need, cause network service is called inside page, but the question is why onAppear is called again – OuSS Oct 15 '22 at 14:14
  • @OuSS you can pass the `currentPage` as `Binding` in each view to run the service when `currentPage` have the same id as the view. – Nhat Nguyen Duc Oct 16 '22 at 02:27
0

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")
        }
vacawama
  • 150,663
  • 30
  • 266
  • 294