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

Posted

技术标签:

【中文标题】如何使 UIViewRepresentable @ViewBuilder 与动态内容一起使用?【英文标题】:How to make a UIViewRepresentable @ViewBuilder work with dynamic content? 【发布时间】:2020-06-15 10:33:37 【问题描述】:

是否可以使带有ViewBuilder 参数的UIViewRepresentable 视图与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() 被调用时,我的动态数据是空的,稍后会被填充或更新。我在初始化时评估我的UIViewRepresentablecontent,而不是在updateUIView() 中更新它。


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

【问题讨论】:

你应该看到这个github.com/Onaeem26/SwiftyUIScrollView。在这里,您可以看到如何通过“更新”处理动态内容。 【参考方案1】:

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

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

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

更新的答案:删除了协调器的解决方案,因为在某些情况下它的工作方式与预期不同

【讨论】:

但是没有协调员如何解决这个问题?【参考方案2】:

以下内容可能会有所帮助。目前尚不清楚缺席CustomUIScrollView 的行为如何(可能问题就在那里),但使用标准UIScrollView 可以与动态 ForEach 一起使用。使用 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)")
                
            
        
    

【讨论】:

什么是\.0?我得到一个范围异常。

以上是关于如何使 UIViewRepresentable @ViewBuilder 与动态内容一起使用?的主要内容,如果未能解决你的问题,请参考以下文章

无法使 UIViewRepresentable 的 CustomView 在 SwiftUI 中正常工作

如何在 SwiftUI 中使 UIViewRepresentable 在 tvOS 上具有焦点?

SwiftUI:如何调整 UIViewRepresentable 输出的大小?

如何在 UIViewRepresentable SwiftUI 中重新加载集合视图

如何在解雇后停止播放 AVPlayerViewController 作为 UIViewRepresentable?

如何在 SwiftUI 中结束 UIViewRepresentable 的编辑?