如何更新嵌入到 UIKit 中的 SwiftUI 视图? [英] How do I update a SwiftUI View that was embedded into UIKit?

查看:33
本文介绍了如何更新嵌入到 UIKit 中的 SwiftUI 视图?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在为 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:

  1. self?.model.text = "Tick 1"(在 ViewController.startTimer() 中).
  2. didChange.send()(在CircleModel.text.didSet中)
  3. Text(model.text) (In CircleView.body)
  1. self?.model.text = "Tick 1" (In ViewController.startTimer()).
  2. didChange.send() (In CircleModel.text.didSet)
  3. Text(model.text) (In CircleView.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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆