FB12795690: The lifecycle of a UIViewControllerRepresentable inside a TabView with a PageTabViewStyle is not being called correctly.
- Date: 2023-7-30
- Resolution: Open
- Area: SwiftUI
- OS: iOS 16.5
- Type: Incorrect/Unexpected Behavior
- Keywords:
Details
Description
When the tabViewStyle modifier for SwiftUI's TabView to something other than normal, the UIViewControllerRepresentable ViewController that configures the TabView will not be able to call viewWillAppear and viewDidAppear.
In other words, the lifecycle methods will not be called properly. This makes it impossible to use UIKit to compensate for the missing parts of the SwiftUI, and also makes it impossible for third-party libraries to work properly.
The sample code that reproduces the problem is as follows, and it reproduces both in Xcode 14.3.1 and Xcode 15 Beta 5. It is also reproduced on both simulators and devices.
import SwiftUI
import UIKit
struct ContentView: View {
var body: some View {
TabView {
SampleView()
Text("Hello, world!")
}
.tabViewStyle(PageTabViewStyle()) // 💥
// .tabViewStyle(.page) // 💥
// .tabViewStyle(.page(indexDisplayMode: .always)) // 💥
// .tabViewStyle(.page(indexDisplayMode: .automatic)) // 💥
// .tabViewStyle(.page(indexDisplayMode: .never)) // 💥
// .tabViewStyle(DefaultTabViewStyle()) // no problem.
}
}
struct SampleView: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> UIViewControllerType {
SampleViewController()
}
func updateUIViewController(_ uiViewController: SampleViewController, context: Context) {
}
func makeCoordinator() -> Coordinator {
Coordinator()
}
class Coordinator {}
}
class SampleViewController: UIViewController {
override func loadView() {
super.loadView()
print("loadView")
let label = UILabel(frame: .init(x: 0, y: 0, width: 100, height: 100))
label.text = "first"
label.font = .systemFont(ofSize: 30)
view.addSubview(label)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
print("willAppear")
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
print("didAppear")
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Development environment
- Xcode 14.3.1 and Xcode 15 beta 5
- iOS 16.4 and 16.5 and 17 beta 4
- device and simulator
Files
I have the exact same issue. I spent four days trying to figure out why my UIViewControllerRepresentable view didn't work as expected. Turns out that viewDidAppear(animated:) is never called, and neither is viewWillAppear(animated:). I wonder if it's because SwiftUI itself provides .onAppear()...
I am facing the same behavior as @mtehsiang
I tried with Xcode 15.2 and iOS 17.2.1, but the lifecycle is still not working.
If this is not fixed, I can't use Google AdMob type custom ViewController in SwiftUI.
I just ran into this issue with Xcode 16 β6 running on both iPadOS 17.6.1 (device) and iPadOS 18 (simulator). Doesn’t seem to be fixed yet.
I have some insights to share on this one. A TabView with PageTabViewStyle seems to be using a UICollectionView under the hood, configured with paging behavior. This means that wrapped view controllers are embedded within collection view cells without really being part of the view controller hierarchy. This, in turn, means that their life cycle methods don’t get called. When scrolling away, their views still remain in the view hierarchy, which is the common behavior of a collection view.
One possible workaround would be to introspect the collection view and hook into its delegate methods for informing about showing/hiding cells. One could also introspect the cells and use the cell’s life cycle methods. I ended up building this component in UIKit using the very same setup but with access to all hooks.
I also reported this as FB16117850 in December and just got a reply on the feedback to retest this with iOS 18.4 beta 1. And it turns out that this is fixed now. :tada:
I tested on iOS 18.4 beta 1 and I'm seeing viewWillAppear now in my representable that's inside List and I also checked Table.
Thanks for sharing your feedback with me🙇♂️. I have not received any feedback back from Apple.
The issue was resolved using Xcode 16.3 beta in combination with iOS 18.4 beta, so I'm closing this.