onAppear is called when the view hasn't appeared

I'm using a 100% SwiftUI app, currently on Xcode 12 Beta 4

In my ContentView, I have a simple TabView

Code Block swift
struct ContentView: View {
var body: some View {
TabView {
AgendaView()
.tabItem {
Image(systemName: "list.bullet")
Text("Agenda")
}
HealthView()
.tabItem {
Image(systemName: "heart")
Text("Health")
}
}
}
}


The AgendaView and HealthView have onAppear methods. On App launch, the AgendaView is the one visible, but onAppear is called for both AgendaView and HealthView.

Why is that? Shouldn't onAppear be called only when the view actually appears on screen?

Code for HealthView and AgendaView

Code Block swift
struct AgendaView: View {
var body: some View {
VStack {
Text("Hello, AgendaView!")
}.onAppear{
print("AgendaView.onAppear")
}.onDisappear(){
print("AgendaView.onDisappear")
}
}
}
struct HealthView: View {
var body: some View {
VStack {
Text("Hello, HealthView!")
}.onAppear{
print("HealthView.onAppear")
}.onDisappear(){
print("HealthView.onDisappear")
}
}
}




Post not yet marked as solved Up vote post of Khuffie Down vote post of Khuffie
14k views

Replies

I am seeing this issue too on: iOS 14.6 Xcode 12.5 macOS 11.4

Xcode 12.5.1, iOS 14.5: the bug is there for me too

Any chance of this being fixed apple?

.onAppear and .onDisappear just don't run for me in Xcode 13 beta 5. If I remove the TabView and have a single page then they work fine.

Any update about this issue?

Seems the bug is fixed in Xcode 13.2 beta (13C5066c) and iOS 15.2.

In my app I use three tabs. My app starts with tab 0. If I go to tab 1, the on onAppear-Method gets called. Last, I go to tab 2 but the onAppear-Method gets called twice. But the method should only called once.

I managed to work it around by adding viewDidAppear callback to SwiftUI, and replacing onAppear callbacks with it.

The code: https://gist.github.com/alongotv/7f450e8c47ed3f057e1f6d35443af269

Usage: apply onDidAppear modifier instead of onAppear to those views you use in TabView.

A rough example:

struct RootView: View {
    @State var selection: Int = 1

    var body: some View {
        TabView(selection: $selection) {
            FirstTabView().tag(1)
            SecondTabView().tag(2)
        }
    }
}

struct FirstTabView: View {

    var body: some View {
        Text("first tab view")
            .onDidAppear {
                // load data
            }
    }
}

struct SecondTabView: View {

    var body: some View {
        Text("second tab view")
            .onDidAppear {
                // load data
            }
    }
}

Hope this helps.

Problems are still there, XCode 12.5 & iOS 14.5 simulator

Issue is still there in XCode 13.2.1 on simulator iOS 15.0.

I'm experimenting this issue in Xcode 14.2 and iOS 16.2...

.onAppear {
      withAnimation(.easeOut(duration: 0.5)) {
        print("IS ANIMATING: \(isAnimating)")
        isAnimating = true
      }
    }
TabView {
      ForEach(0..<5) { item in 
        FruitCardView()
      } //: FOREACH
    } //: TABVIEW
    .tabViewStyle(PageTabViewStyle())
    .padding(.vertical, 20)

That onAppear is for a ZStack. And this is the logs that prove onAppear is called before the item is visible in the TabView...

SOLUTION:

Used .task instead of .onAppear. .task runs whenever the view appears so it works the same without bugging out. .onDisappear still works fine so these can be used together to take the place of the paired usage of .onAppear and .onDisappear until there is a fix for .onAppear

(Tagging this for some poor soul who had my problem: Camera indicator turning on in view that doesn't use camera SwiftUI)

  • @okitobi .task and .onAppear works the same for TabView - all called when the view hasn't appeared

Add a Comment