【问题标题】:In SwiftUI, how to use UIHostingController inside an UIView or as an UIView?在 SwiftUI 中,如何在 UIView 内或作为 UIView 使用 UIHostingController?
【发布时间】:2019-11-11 03:07:09
【问题描述】:

另一个问题标题可能是“如何将 UIHostingController 的视图添加为 UIView 的子视图?”

我正在创建一个新的 UI 组件,并且很想尝试一下 SwiftUI。下图是当前的视图结构。 UIView 是我现在使用的(右上),SwiftUI 视图是我尝试使用的(右下)。

在我观看了 WWDC 2019 的所有 SwiftUI 视频之后。我仍然不知道如何使用 SwiftUI 视图并将其放在 UIView 实例应该去的位置。

我从“集成 SwiftUI”演讲中注意到,有一个用于 macOS 的 NSHostingViewhttps://developer.apple.com/documentation/swiftui/nshostingview#,这让我想知道是否有类似的东西,或者有什么替代方法可以实现它。

我读到类似Include SwiftUI views in existing UIKit application 的问题提到SwiftUI 和UIKit 可以与UIHostingController 一起玩。但是,我想做的是只采用一小部分 SwiftUI 并将其放入我现有的 UIKit 视图组件中,而不是将其用作控制器。

我是 iOS 开发的新手,如果有办法可以将视图控制器用作 UIView 视图,请发表评论。谢谢。

【问题讨论】:

标签: ios swift uiview uiviewcontroller swiftui


【解决方案1】:

视图控制器不仅仅用于顶级场景。我们经常将视图控制器放在视图控制器中。它被称为“视图控制器包含”和/或“子视图控制器”。 (顺便说一句,视图控制器容器通常是对抗传统 UIKit 应用程序中视图控制器膨胀的好方法,将复杂的场景分解为多个视图控制器。)

所以,

  • 继续使用UIHostingController

    let controller = UIHostingController(rootView: ...)
    

    和;

  • 添加视图控制器然后可以将宿主控制器添加为子视图控制器:

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

    显然,您还需要为主机控制器的 view 设置 frame 或布局约束。

    请参阅UIViewControllerdocumentation实现容器视图控制器部分,了解有关将一个视图控制器嵌入另一个视图控制器的一般信息。


例如,假设我们有一个 SwiftUI 视图来渲染一个带有文本的圆圈:

struct CircleView : View {
    @ObservedObject var model: CircleModel

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

假设这是我们视图的模型:

import Combine

class CircleModel: ObservableObject {
    @Published var text: String

    init(text: String) {
        self.text = text
    }
}

然后我们的 UIKit 视图控制器可以添加 SwiftUI 视图,在 UIView 中设置其框架/约束,并根据需要更新其模型:

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)"
        }
    }
}

【讨论】:

  • 我遇到的问题是 UIHostingController 正在缩小到一个尺寸,小于 SwiftUI 视图。它嵌入到 ScrollView ContentView 中,所以我认为它必须“传达”它的内在/最小尺寸,但我不太确定如何。
  • @Moritz Mahringer 您可能需要限制UIHostingController 的视图或简单地设置其框架。请注意,Rob 的示例为主机控制器的视图设置了约束。
  • 感谢您解决“在层次结构中安装视图后如何更新视图状态”的问题。我希望有一个比@ObservedObject 更简单的解决方案。例如。也许在CircleView 上定义@State var text:String 之类的东西,并完全消除对CircleModel 的需求,但是如果创建ObservableObject 是唯一的方法,那就这样吧。 -- 然而,即使 ObservableObject 解决方案似乎也不起作用...text 属性已正确更新,didChange.send() 被调用,但这永远不会导致 body 再次触发.
  • 我在这里作为一个单独的问题提出了这个问题:stackoverflow.com/questions/59219019/…
  • 关于使用视图控制器包含来分解大视图控制器的要点。我已经看到人们发明了一些疯狂的模式来尝试以其他方式解决它!
【解决方案2】:

我有一些想法。

  1. UIHostingController 包裹 SwiftUI
  2. 初始化控制器
  3. 将新控制器添加为子视图控制器
  4. 将控制器视图作为子视图添加到它应该去的地方

因此:

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

一个视图控制器总是使用其他视图控制器作为视图。

斯坦福 CS193P,https://youtu.be/w7a79cx3UaY?t=679

参考

【讨论】:

    猜你喜欢
    • 2020-06-17
    • 1970-01-01
    • 1970-01-01
    • 2021-01-19
    • 2022-11-05
    • 1970-01-01
    • 2017-09-25
    • 2019-01-30
    • 1970-01-01
    相关资源
    最近更新 更多