简体   繁体   English

如何使 UIViewRepresentable @ViewBuilder 与动态内容一起使用?

[英]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() .我在初始化时评估我的UIViewRepresentablecontent ,而不是在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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM