[英]How to make a UIViewRepresentable @ViewBuilder work with dynamic content?
Is it possible to make a UIViewRepresentable
view which takes a ViewBuilder
argument work with dynamic content such as ForEach
loops?是否可以使带有
ViewBuilder
参数的UIViewRepresentable
视图与动态内容(例如ForEach
循环)一起使用?
I have the following UIViewRepresentable
view which I'm using to drop down to UIKit
and get some custom UIScrollView
behaviour:我有以下
UIViewRepresentable
视图,我用它来下拉到UIKit
并获得一些自定义UIScrollView
行为:
struct CustomScrollView<Content:View>: UIViewRepresentable {
private let content: UIView
private let scrollView = CustomUIScrollView()
init(@ViewBuilder content: () -> Content) {
self.content = UIHostingController(rootView: content()).view
self.content.backgroundColor = .clear
}
func makeUIView(context: Context) -> UIView {
scrollView.addSubview(content)
// ...
return scrollView
}
func updateUIView(_ uiView: UIView, context: Context) {}
}
This works fine with static content as follows:这适用于 static 内容,如下所示:
var body: some View {
CustomScrollView {
VStack {
ForEach(1..<50) { number in
Text(String(number))
}
}
}
}
But it fails with dynamic content, showing a blank view:但它因动态内容而失败,显示空白视图:
var body: some View {
CustomScrollView {
VStack {
ForEach(self.numbers) { number in
Text(String(number))
}
}
}
}
I understand that this is because when makeUIView()
is called my dynamic data is empty, and it is later filled or updated.我知道这是因为当
makeUIView()
被调用时,我的动态数据是空的,稍后会被填充或更新。 I evaluate my UIViewRepresentable
's content
at init, and don't update it in updateUIView()
.我在初始化时评估我的
UIViewRepresentable
的content
,而不是在updateUIView()
中更新它。
How do you go about updating dynamic child content in updateUIView()
?您如何 go 关于在
updateUIView()
中更新动态子内容? I tried capturing the @ViewBuilder
parameter as an @escaping
closure and evaluating it every time updateUIView()
is called, which seems like the right solution (albeit inefficient?), but no luck so far.我尝试将
@ViewBuilder
参数捕获为@escaping
闭包并在每次调用updateUIView()
时对其进行评估,这似乎是正确的解决方案(尽管效率低下?),但到目前为止还没有运气。
The following might be helpful.以下内容可能会有所帮助。 It is not clear how absent
CustomUIScrollView
behaves (probably the issue is there), but using standard UIScrollView
works with dynamic ForEach.目前尚不清楚
CustomUIScrollView
的缺失如何表现(可能存在问题),但使用标准UIScrollView
可以与动态 ForEach 一起使用。 Tested with Xcode 11.4 / iOS 13.4使用 Xcode 11.4 / iOS 13.4 测试
struct CustomScrollView<Content:View>: UIViewRepresentable {
private let content: UIView
private let scrollView = UIScrollView()
init(@ViewBuilder content: () -> Content) {
self.content = UIHostingController(rootView: content()).view
self.content.backgroundColor = .clear
}
func makeUIView(context: Context) -> UIView {
content.translatesAutoresizingMaskIntoConstraints = false
scrollView.addSubview(content)
let constraints = [
content.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
content.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor),
content.topAnchor.constraint(equalTo: scrollView.contentLayoutGuide.topAnchor),
content.bottomAnchor.constraint(equalTo: scrollView.contentLayoutGuide.bottomAnchor),
content.widthAnchor.constraint(equalTo: scrollView.widthAnchor)
]
scrollView.addConstraints(constraints)
return scrollView
}
func updateUIView(_ uiView: UIView, context: Context) {}
}
struct TestCustomScrollView: View {
private var items = Array(repeating: "Test", count: 50)
var body: some View {
CustomScrollView {
VStack {
ForEach(Array(items.enumerated()), id: \.0) { i, item in
Text("\(item) - \(i)")
}
}
}
}
}
Evaluation of @ViewBuilder fails because you mutating the wrong copy of the struct here @ViewBuilder 的评估失败,因为您在此处更改了错误的结构副本
func updateUIView(_ uiView: UIView, context: Context) {}
You should mutate uiView
's subviews directly with the content()
您应该直接使用
content()
改变uiView
的子视图
Updated Answer: Removed solution with a coordinator, because in some cases it works not as expected更新的答案:使用协调器删除了解决方案,因为在某些情况下它的工作方式不如预期
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.