uiviewcontroller - 我应该在 SwiftUI 中的 updateUIViewController(_:context:) 内调用 viewDidLoad()
问题描述
我创建UIScrollView
要集成到 SwiftUI 视图中。它包含UIHostingController
托管 SwiftUI 视图。当我更新UIHostingController
时,UIScrollView
不会改变它的约束。我既不能滚动到顶部也不能滚动到底部。当我尝试调用viewDidLoad()
insideupdateUIViewController(_:context:)
时,它会像我预期的那样工作。这是我的示例代码,
struct ContentView: View {
@State private var max = 100
var body: some View {
VStack {
Button("Add") { self.max += 2 }
ScrollableView {
ForEach(0..<self.max, id: \.self) { index in
Text("Hello \(index)")
.frame(width: UIScreen.main.bounds.width, height: 100)
.background(Color(red: Double.random(in: 0...255) / 255, green: Double.random(in: 0...255) / 255, blue: Double.random(in: 0...255) / 255))
}
}
}
}
}
class ScrollViewController<Content: View>: UIViewController, UIScrollViewDelegate {
var hostingController: UIHostingController<Content>! = nil
init(rootView: Content) {
self.hostingController = UIHostingController<Content>(rootView: rootView)
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
var scrollView: UIScrollView = UIScrollView()
override func viewDidLoad() {
self.view = UIView()
self.addChild(hostingController)
view.addSubview(scrollView)
scrollView.addSubview(hostingController.view)
scrollView.delegate = self
scrollView.scrollsToTop = true
scrollView.isScrollEnabled = true
makeConstraints()
hostingController.didMove(toParent: self)
}
func makeConstraints() {
scrollView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
scrollView.heightAnchor.constraint(equalTo: view.heightAnchor).isActive = true
hostingController.view.widthAnchor.constraint(equalTo: scrollView.widthAnchor).isActive = true
hostingController.view.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
hostingController.view.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
hostingController.view.translatesAutoresizingMaskIntoConstraints = false
scrollView.translatesAutoresizingMaskIntoConstraints = false
}
}
struct ScrollableView<Content: View>: UIViewControllerRepresentable {
var content: () -> Content
init(@ViewBuilder content: @escaping () -> Content) {
self.content = content
}
func makeUIViewController(context: Context) -> ScrollViewController<Content> {
let vc = ScrollViewController(rootView: self.content())
return vc
}
func updateUIViewController(_ viewController: ScrollViewController<Content>, context: Context) {
viewController.hostingController.rootView = self.content()
viewController.viewDidLoad()
}
}
我不认为这是一个好方法。我想知道是否有更新控制器的最佳方法。如果有人知道最好的解决方案,请分享我。谢谢。
解决方案
你是对的,我们永远不应该调用我们自己的viewDidLoad
.
让我们使用视图调试器诊断问题。因此,例如,这里是(设置max
为8
使其易于管理):
注意主机控制器的高度view
是 800(因为我们有 8 个子视图,每个 100 pt)。到目前为止,一切都很好。
现在点击“添加”按钮并重复:
我们可以看到问题不在于滚动视图,而在于宿主视图控制器的视图。即使现在有 10 个项目,它仍然认为宿主视图控制器的视图高度是 800。
所以,我们可以打电话setNeedsUpdateConstraints
来解决问题:
func updateUIViewController(_ viewController: ScrollViewController<Content>, context: Context) {
viewController.hostingController.rootView = content()
viewController.hostingController.view.setNeedsUpdateConstraints()
}
因此:
struct ContentView: View {
@State private var max = 8
var body: some View {
GeometryReader { geometry in // don't reference `UIScreen.main.bounds` as that doesn’t work in split screen multitasking
VStack {
Button("Add") { self.max += 2 }
ScrollableView {
ForEach(0..<self.max, id: \.self) { index in
Text("Hello \(index)")
.frame(width: geometry.size.width, height: 100)
.background(Color(red: .random(in: 0...1), green: .random(in: 0...1), blue: .random(in: 0...1)))
}
}
}
}
}
}
class ScrollViewController<Content: View>: UIViewController {
var hostingController: UIHostingController<Content>! = nil
init(rootView: Content) {
self.hostingController = UIHostingController<Content>(rootView: rootView)
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
var scrollView = UIScrollView()
override func viewDidLoad() {
super.viewDidLoad() // you need to call `super`
// self.view = UIView() // don't set `self.view`
addChild(hostingController)
view.addSubview(scrollView)
scrollView.addSubview(hostingController.view)
// scrollView.delegate = self // you're not currently using this delegate protocol, so we probably shouldn't set the delegate
// scrollView.scrollsToTop = true // these are the default values
// scrollView.isScrollEnabled = true
makeConstraints()
hostingController.didMove(toParent: self)
}
func makeConstraints() {
NSLayoutConstraint.activate([
// constraints for scroll view w/in main view
scrollView.topAnchor.constraint(equalTo: view.topAnchor),
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
// define contentSize of scroll view relative to hosting controller's view
hostingController.view.topAnchor.constraint(equalTo: scrollView.contentLayoutGuide.topAnchor),
hostingController.view.bottomAnchor.constraint(equalTo: scrollView.contentLayoutGuide.bottomAnchor),
hostingController.view.leadingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.leadingAnchor),
hostingController.view.trailingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.trailingAnchor)
])
hostingController.view.translatesAutoresizingMaskIntoConstraints = false
scrollView.translatesAutoresizingMaskIntoConstraints = false
}
}
struct ScrollableView<Content: View>: UIViewControllerRepresentable {
var content: () -> Content
init(@ViewBuilder content: @escaping () -> Content) {
self.content = content
}
func makeUIViewController(context: Context) -> ScrollViewController<Content> {
ScrollViewController(rootView: content())
}
func updateUIViewController(_ viewController: ScrollViewController<Content>, context: Context) {
viewController.hostingController.rootView = content()
viewController.hostingController.view.setNeedsUpdateConstraints()
}
}
推荐阅读
- c - 在尝试分配新内存之前是否重新分配?
- c++ - 如何将 x 和 y 包裹在屏幕上?
- python - sp_execute_external_script 找不到 setuptools 安装的模块
- python-3.x - MongoEngine:是否以及如何清理搜索和数据输入?
- python - 绘制元组列表(包含图号)
- python - 带有两个 y 轴的 Pandas 中的 Plot():第二行存储在哪里?
- node.js - 节点找不到 perf_hooks 模块
- typescript - 基于变量的条件类型
- python - Python Spark - 将时间戳转换为 YYYY-MM-DD
- react-native - 应用程序在后台时Websocket断开连接(REACT NATIVE)