In SwiftUI, how to use UIHostingController inside an UIView or as an UIView?

后端 未结 2 2026
-上瘾入骨i
-上瘾入骨i 2020-12-24 14:17

An alternative question title could be \"How to add an UIHostingController\'s view as subview for an UIView?\".

I am creating a new piece of UI comp

相关标签:
2条回答
  • 2020-12-24 14:27

    View controllers are not just for the top level scene. We often place view controllers within view controllers. It’s called “view controller containment” and/or “child view controllers”. (BTW, view controller containers are, in general, a great way to fight view controller bloat in traditional UIKit apps, breaking complicated scenes into multiple view controllers.)

    So,

    • Go ahead and use UIHostingController:

      let controller = UIHostingController(rootView: ...)
      

      and;

    • Add the view controller can then add the hosting controller as a child view controller:

      addChild(controller)
      view.addSubview(controller.view)
      controller.didMove(toParent: self)
      

      Obviously, you’d also set the frame or the layout constraints for the hosting controller’s view.

      See the Implementing a Container View Controller section of the UIViewController documentation for general information about embedding one view controller within another.


    For example, let’s imagine that we had a SwiftUI View to render a circle with text in it:

    struct CircleView : View {
        @ObservedObject var model: CircleModel
    
        var body: some View {
            ZStack {
                Circle()
                    .fill(Color.blue)
                Text(model.text)
                    .foregroundColor(Color.white)
            }
        }
    }
    

    And let’s say this was our view’s model:

    import Combine
    
    class CircleModel: ObservableObject {
        @Published var text: String
    
        init(text: String) {
            self.text = text
        }
    }
    

    Then our UIKit view controller could add the SwiftUI view, set its frame/constraints within the UIView, and update its model as you see fit:

    import UIKit
    import SwiftUI
    
    class ViewController: UIViewController {
        private weak var timer: Timer?
        private var model = CircleModel(text: "")
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            addCircleView()
            startTimer()
        }
    
        deinit {
            timer?.invalidate()
        }
    }
    
    private extension ViewController {
        func addCircleView() {
            let circleView = CircleView(model: model)
            let controller = UIHostingController(rootView: circleView)
            addChild(controller)
            controller.view.translatesAutoresizingMaskIntoConstraints = false
            view.addSubview(controller.view)
            controller.didMove(toParent: self)
    
            NSLayoutConstraint.activate([
                controller.view.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.5),
                controller.view.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.5),
                controller.view.centerXAnchor.constraint(equalTo: view.centerXAnchor),
                controller.view.centerYAnchor.constraint(equalTo: view.centerYAnchor)
            ])
        }
    
        func startTimer() {
            var index = 0
            timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] _ in
                index += 1
                self?.model.text = "Tick \(index)"
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-24 14:32

    I have some idea in mind.

    1. Wrap the SwiftUI with a UIHostingController
    2. Initialize the controller
    3. Add the new controller as a child view controller
    4. Add the controller view as a subview to where it should go

    Thus:

    addChild(hostingViewController)
    hostingViewController.view.frame = ...
    view.addSubview(hostingViewController.view)
    hostingViewController.didMove(toParent: self)
    

    A view controller always uses other view controllers as views.

    Stanford CS193P, https://youtu.be/w7a79cx3UaY?t=679

    Reference

    • How to add an UIViewController's view as subview
    • Place UIViewController inside UIView
    • Use UIViewController as TableView cell
    0 讨论(0)
提交回复
热议问题