如何更新嵌入到 UIKit 中的 SwiftUI 视图? [英] How do I update a SwiftUI View that was embedded into UIKit?
问题描述
我正在为 UIKit 寻找等效于 AppKit 的 NSHostingView 以便我可以在 UIKit 中嵌入 SwiftUI 视图.不幸的是,UIKit 没有与 NSHostingView
等效的类.与 NSHostingController 等价的最接近的,名为 UIHostingController.由于视图控制器包含视图,我们应该能够调用适当的 UIViewController 嵌入方法,然后抓取查看
并直接使用.
I'm looking for an equivalent of AppKit's NSHostingView for UIKit so that I can embed a SwiftUI view in UIKit. Unfortunately, UIKit does not have an equivalent class to NSHostingView
. The closest we have as an equivalent of NSHostingController, named UIHostingController. Since a view controller contains a view, we should be able to call the appropriate UIViewController embedding methods, and then grab the view
and use it directly.
那里 是 许多 文章 说明这是在 UIKit 中嵌入 SwiftUI 视图的方式.但是,他们通常无法解释您将如何从 UIKit ➡️ SwiftUI 进行通信.例如,假设我实现了一个用作进度条的 SwiftUI 视图,我希望定期更新进度.我希望我的旧版/UIKit 代码更新 SwiftUI 视图以显示新进度.
There are many articles that explain that this is the way to embed a SwiftUI view inside UIKit. However, they typically fall short in explaining how you would communicate from UIKit ➡️ SwiftUI. For example, imagine I implemented a SwiftUI view that acts as a progress bar, periodically, I'd like the progress to be updated. I want my legacy/UIKit code to update the SwiftUI view to display the new progress.
我发现的唯一一篇文章几乎解释了如何操作嵌入视图的内容,建议我们使用@ObservedObject
:
The only article I found that came close to explaining how to manipulate an embedded view's content suggested we do so by using @ObservedObject
:
import UIKit
import SwiftUI
import Combine
class CircleModel: ObservableObject {
var didChange = PassthroughSubject<Void, Never>()
var text: String { didSet { didChange.send() } }
init(text: String) {
self.text = text
}
}
struct CircleView : View {
@ObservedObject var model: CircleModel
var body: some View {
ZStack {
Circle()
.fill(Color.blue)
Text(model.text)
.foregroundColor(Color.white)
}
}
}
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)"
}
}
}
这似乎是有道理的,因为计时器应该触发更新视图的一系列事件:
This seems to make sense as the timer should trigger a chain of events that update the view:
- ✅
self?.model.text = "Tick 1"
(在ViewController.startTimer()
中). - ✅
didChange.send()
(在CircleModel.text.didSet
中) - ❌
Text(model.text)
(InCircleView.body
)
- ✅
self?.model.text = "Tick 1"
(InViewController.startTimer()
). - ✅
didChange.send()
(InCircleModel.text.didSet
) - ❌
Text(model.text)
(InCircleView.body
)
正如您通过指标(指定是否运行某事)所见,问题在于 didChange.send()
永远不会触发 CircleView.body 的重新运行代码>.
As you can see by the indicators (which specify if something was run or not), the problem is that didChange.send()
never triggers a re-run of CircleView.body
.
如何从 UIKit > SwiftUI 进行通信以操作嵌入在 UIKit 中的 SwiftUI 视图?
How do I communicate from UIKit > SwiftUI to manipulate a SwiftUI view that was embedded in UIKit?
推荐答案
你只需要扔掉那个自定义主题,使用标准的@Published
,如下
All you need is to throw away that custom subject, and use standard @Published
, as below
class CircleModel: ObservableObject {
@Published var text: String
init(text: String) {
self.text = text
}
}
测试:Xcode 11.2/iOS 13.2
这篇关于如何更新嵌入到 UIKit 中的 SwiftUI 视图?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!