Segue To UIViewController From SwiftUI View
Asked Answered
C

2

13

I am working to implement some SwiftUI content into my existing app. I currently have a UIViewController, which hosts a MTKView for camera preview.

I have created a new SwiftUI view, which is now my root view, as set in my SceneDelegate.swift file. The SwiftUI view loads at launch, as expected. Now, I would like to create a segue in which, when a user taps on a row in my List, it will segue, full-screen to my existing UIViewController. Here is how I'm calling that;

var body: some View {
    VStack {
        NavigationView {
            List(sessionTypes) { session in
                NavigationLink(destination: CameraControllerWrapper()) {
                    SessionRow(session: session)
                    .frame(height: 40.0)
                }
            }
        .navigationBarTitle(Text("Camera Types"))
        }
    }
}

For posterity, here is my CameraControllerWrapper UIViewControllerRepresentable;

struct CameraControllerWrapper: UIViewControllerRepresentable {
typealias UIViewControllerType = CameraController

   func makeUIViewController(context: UIViewControllerRepresentableContext<CameraControllerWrapper>) -> CameraControllerWrapper.UIViewControllerType {
    return CameraController()
   }

   func updateUIViewController(_ uiViewController: CameraControllerWrapper.UIViewControllerType, context: UIViewControllerRepresentableContext<CameraControllerWrapper>) {
    //
   }
}

While this "works," my app crashes as soon as the CameraController is called, as it seems any of my IBOutlets cannot be found. CameraController is a UIViewController built in the storyboard.

Collen answered 17/9, 2019 at 0:21 Comment(2)
For clarity, are you saying you have (1) a Storyboard app, that you (2) made the MainViewController root view be a SwiftUI view, and (3) put a NavigationView in this root view and (4) navigates to a UIViewControllerRepresentable that has (5) IBOutlets defined in it? If something in this chain is wrong, please correct me.Audrey
I think that's correct. I have a UIViewController that was built and designed in a Storyboard, with IBOutlets, and was previously my entry view controller. I've now added a SwiftUI view that is my root view, and would like to segue to the previous UIViewController, which is now a "second" view controller, no longer the main.Collen
C
13

I managed to resolve this by realizing that I needed to instantiate the UIViewController from the Storyboard, and not in code (as I had built its layout in Storyboard, alongside some programatic elements). To use the above NavigationLink, I needed to adjust my UIViewControllerRepresentable as such:

struct CameraControllerWrapper: UIViewControllerRepresentable {
    typealias UIViewControllerType = CameraController
    
    func makeUIViewController(context: UIViewControllerRepresentableContext<CameraControllerWrapper>) -> CameraControllerWrapper.UIViewControllerType {    
        let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
        let mainViewController: CameraController = mainStoryboard.instantiateViewController(withIdentifier: "CameraController") as! CameraController
        return mainViewController
    }

    func updateUIViewController(_ uiViewController: CameraControllerWrapper.UIViewControllerType, context: UIViewControllerRepresentableContext<CameraControllerWrapper>) {
        //
    }
}
Collen answered 17/9, 2019 at 17:58 Comment(0)
D
0

A few potential optimizations for @ZbadhabitZ answer, for people who may be relatively new to Swift, starting with a brief example.

extension NotesVC : UIViewControllerRepresentable {

    func makeUIViewController(context: Context) -> NotesVC {
        let storyboard =  UIStoryboard(name: "Main", bundle: nil)
        let vc = storyboard.instantiateViewController(withIdentifier: "NotesVC") 
        return vc as! NotesVC
    }

    func updateUIViewController(_ vc: NotesVC, context: Context) {
        // your stuff here
    }
}
  1. Consider adding Swift Protocols as extensions. It can be tidier and more flexible (including being able to adapt a class to a protocol by defining the protocol extension in a separate file entirely). For example, I recently tried out a 3rd party tab controller that required adapting my class to a protocol and the instructions entailed editing several files both added and my own and I was able to put 80% of it into a separate file as extensions and didn't have to muck up my code while experimenting with the new stuff, and it was easy to remove when I was done with it.

  1. Function arguments with the heavy typing are overkill; or confusing, at least.

    Apple Developer demo shows an example with PageViewController that shows all the extra typing isn't necessary:


  1. The original answer's code doesn't make use of Swift implied types. For example, rather

     let flag : Bool = false
    

    Instead write:

     let flag = false
    

    flag will be typed as Bool implicitly in that declaration. That is because false always implies Bool. Just as 1.0 always implies Double by default, and so on. The payoff gets even bigger when you start getting into longer class names.

Demented answered 27/4, 2022 at 20:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.