iOS 14 .onAppear() is called on DISappear instead of appear

I've got a View with
Code Block
.onAppear(perform: {
print("appear")
})
.onDisappear(perform: {
print("disappear")
})
set on it.

In iOS 13.5, when I navigate away from the view via a NavigationLink, it prints "disapear"; when I close the new view and get back to the first view "appear" gets printed. All as it should be.

Now in iOS 14, this happens instead:
  • when the view is first shown, it prints "appear"

  • when navigating away from the view, it prints "disappear" and "appear"

  • when navigating back to the view, nothing gets printed

  • when navigating away from the view, then "appear" gets printed, along with the expected "disappear"

...is this some new super-lazy way of doing things or just plain a bug?

It runs havoc with my login, as I set @Binding vars in the second view, which are evaluated back in the first view when .onAppear() runs. Worked great until iOS 14...
Hi I am following the iOS App Dev Tutorials and I found this bug using Xcode 12.5.

Code Block
.onAppear {
            scrumTimer.reset(lengthInMinutes: scrum.lengthInMinutes, attendees: scrum.attendees)
            scrumTimer.startScrum()
            scrumTimer.speakerChangedAction = {
                player.seek(to: .zero)
                player.play()
            }
        }
        .onDisappear {
            scrumTimer.stopScrum()
            let newHistory = History(attendees: scrum.attendees, lengthInMinutes: scrumTimer.secondsElapsed / 60)
            scrum.history.insert(newHistory, at: 0)


The results is that when the view is shown, the onAppear is executed, then the onDisappear is executed and then again the onAppear. The results is that I have in the history 2 copies of the same entry.


I just updated to iOS 14.6 and it still doesn't work :(

Just started working through the SwiftUI tutorial for iOS on the apple developer website. Freshly installed xCode on a new M1 mac... onAppear AND onDisappear would trigger on appearance, and both would trigger AGAIN on disappearance.

Opened up the console and saw this assertion error

displayModeButtonItem is internally managed and not exposed for DoubleColumn style

People had similar issues with that error, adding this to the root NavigationView fixed the error for my use case

.navigationViewStyle(StackNavigationViewStyle())

Hope this helps someone!

onAppear and onDisappear don't work. However, this customized function did work!

extension View {
  func onDidAppear(_ perform: @escaping (() -> Void)) -> some View {
    self.modifier(ViewDidAppearModifier(callback: perform))
  }
}

struct ViewDidAppearModifier: ViewModifier {
  let callback: () -> Void

  func body(content: Content) -> some View {
    content
      .background(ViewDidAppearHandler(onDidAppear: callback))
  }
}

struct ViewDidAppearHandler: UIViewControllerRepresentable {
  func makeCoordinator() -> ViewDidAppearHandler.Coordinator {
    Coordinator(onDidAppear: onDidAppear)
  }

  let onDidAppear: () -> Void

  func makeUIViewController(context: UIViewControllerRepresentableContext<ViewDidAppearHandler>) -> UIViewController {
    context.coordinator
  }

  func updateUIViewController(_ uiViewController: UIViewController, context: UIViewControllerRepresentableContext<ViewDidAppearHandler>) {
  }

  typealias UIViewControllerType = UIViewController

  class Coordinator: UIViewController {
    let onDidAppear: () -> Void

    init(onDidAppear: @escaping () -> Void) {
      self.onDidAppear = onDidAppear
      super.init(nibName: nil, bundle: nil)
    }

    required init?(coder: NSCoder) {
      fatalError("init(coder:) has not been implemented")
    }

    override func viewDidAppear(_ animated: Bool) {
      super.viewDidAppear(animated)
      onDidAppear()
    }
  }
}

I have the same issue like the OP, and it still exists in iOS 16 beta 2, whether I use NavigationSplitView or NavigationView with column-style. Are there no other view modifiers that are called? I have a singleton audio engine in the app, and the detail view initializes itself on the engine at .onAppear. Can't have a new audio engine for each detail view, especially if the previous detail view is not discarded.

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)

iOS 14 .onAppear() is called on DISappear instead of appear
 
 
Q