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)"
}
}
}
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…