2

I have found a minimal SwiftUI app that exhibits a bug:

class HelloWorldVC: UIViewController {
    override func loadView() {
        super.loadView()
        let label = UILabel()
        label.text = "Hello, World"
        view.addSubview(label)

        label.translatesAutoresizingMaskIntoConstraints = false
        label.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true
        label.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true
        label.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
        label.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
    }
}

struct ViewControllerContainer: UIViewControllerRepresentable {
    let vc: UIViewController

    func makeUIViewController(context: Context) -> some UIViewController { vc }
    func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {}
}

struct ContentView: View {
    var body: some View {
        NavigationView {

            // Works:
            //NavigationLink("View UIKit VC", destination: ViewControllerContainer(vc: HelloWorldVC()))

            // Only loads the UIKit view once. It's blank on subsequent tries.
            NavigationLink(
                "Detail Screen",
                destination: NavigationLink(
                    "View UIKit VC",
                    destination: ViewControllerContainer(vc: HelloWorldVC())
                )
            )
        }
    }
}

Steps to reproduce:

  • Tap "Detail Screen"
  • Tap "View UIKit VC". You will see the "Hello, World" UIViewController.
  • Tap Back
  • Tap "View UIKit VC"

Expected:

  • You should see the "Hello, World" UIViewController again

Actual:

  • You will see a blank view. This will happen as many times as you try.

Blank screen seen from 2nd try on

Note: In the commented out code, it works properly if you only have one layer deep of NavigationLink.

Jeremy
  • 2,801
  • 2
  • 30
  • 31

1 Answers1

1

You are not working with UIViewControllerRepresentable correctly. You need to create a new view controller inside makeUIViewController, reusing it breaks the view controller lifecycle in your case.

The UIViewControllerRepresentable properties can be passed to the view controller when you create or update it, as follows:

struct ViewControllerContainer: UIViewControllerRepresentable {
    let props: Int

    func makeUIViewController(context: Context) -> some UIViewController { 
        HelloWorldVC(props: props) 
    }
    func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
        uiViewController.props = props
    }
}
Phil Dukhov
  • 67,741
  • 15
  • 184
  • 220
  • You are totally right. I feel dumb for not realizing this earlier and spending all the time filing the bug. Thank you! – Jeremy Sep 13 '21 at 15:09