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

时间:2020-06-15 10:33:37

标签: ios swift uikit swiftui

是否可以制作一个UIViewRepresentable的视图,该视图使用ViewBuilder参数处理动态内容,例如ForEach循环?

我有以下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) {}

}

此方法可用于静态内容,如下所示:

var body: some View {
    CustomScrollView {
        VStack {
            ForEach(1..<50) { number in
                Text(String(number))
            }
        }
    }
}

但是它无法显示动态内容,显示空白视图:

var body: some View {
    CustomScrollView {
        VStack {
            ForEach(self.numbers) { number in
                Text(String(number))
            }
        }
    }
}

我了解这是因为调用makeUIView()时,我的动态数据为空,后来被填充或更新。我在初始化时评估了UIViewRepresentable的{​​{1}},并且没有在content中对其进行更新。


您如何在updateUIView()中更新动态子内容?我尝试将updateUIView()参数捕获为@ViewBuilder闭包,并在每次调用@escaping时对其进行评估,这似乎是正确的解决方案(尽管效率不高?),但到目前为止还没有运气。 / p>

2 个答案:

答案 0 :(得分:1)

以下内容可能会有所帮助。目前尚不清楚CustomUIScrollView的表现如何(可能存在问题),但是使用标准UIScrollView可以与动态ForEach一起使用。使用Xcode 11.4 / iOS 13.4进行了测试

demo

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

答案 1 :(得分:1)

@ViewBuilder的评估失败,因为您在此处突变了该结构的错误副本

func updateUIView(_ uiView: UIView, context: Context) {}

您应该直接使用uiView来更改content()的子视图

更新后的答案:删除了带有协调器的解决方案,因为在某些情况下,该解决方案无法按预期运行