如何用SwiftUI实现UIViewRepresentable的延迟加载ScrollView?

lhcgjxsq  于 2023-03-28  发布在  Swift
关注(0)|答案(1)|浏览(185)

我尝试使用UIViewRepresentable实现一个延迟加载的MyLazyHStack视图,但是它没有延迟加载元素。我在MyLazyHStack的内容中使用LazyHStack,但是所有元素都是一次加载的,而不是根据需要延迟加载。这里是我的代码的一个最小示例。有人能帮助我找出我做错了什么吗?

struct MyLazyHStack<Content: View>: UIViewRepresentable {
    var content: Content
    
    func makeUIView(context: Context) -> UIView {
        let scrollView = UIScrollView()
        scrollView.isPagingEnabled = true
        
        let hostingController = UIHostingController(rootView: content)
        hostingController.view.translatesAutoresizingMaskIntoConstraints = false
        
        scrollView.addSubview(hostingController.view)
        
        NSLayoutConstraint.activate([
            hostingController.view.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
            hostingController.view.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor),
            hostingController.view.topAnchor.constraint(equalTo: scrollView.topAnchor),
            hostingController.view.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
            hostingController.view.heightAnchor.constraint(equalTo: scrollView.heightAnchor)
        ])
        
        return scrollView
    }
    
    func updateUIView(_ uiView: UIView, context: Context) {}
}

struct ContentView: View {
    var body: some View {
        MyLazyHStack {
            LazyHStack {
                ForEach(1..<100) { index in
                    Text("Element \(index)")
                }
            }
        }
    }
}
taor4pac

taor4pac1#

要使用UIViewRepresentable实现延迟加载MyLazyHStack视图,需要对代码进行一些更改。
1.将contentSize变量添加到UIViewRepresentable结构中以跟踪内容的大小。
1.实现UIScrollViewDelegate协议的scrollViewDidScroll方法,以检测用户何时滚动到可见区域的末尾。
1.更新UIViewRepresentable结构的updateUIView方法,以计算内容的可见区域,并仅加载可见区域内的元素。
对原始代码的更改:
1.添加contentSize变量以跟踪内容的大小。
1.将UIScrollView的委托设置为Coordinator对象,以便在用户滚动时调用scrollViewDidScroll方法。
1.更新updateUIView方法以计算内容的可见区域,并仅加载可见区域内的元素。
1.添加一个符合UIScrollViewDelegate协议并实现scrollViewDidScroll方法的Coordinator类。该方法在用户滚动时更新超级视图的布局。

struct MyLazyHStack<Content: View>: UIViewRepresentable {
    var content: Content
    func makeUIView(context: Context) -> UIScrollView {
        let scrollView = UIScrollView()
        scrollView.isPagingEnabled = true
        scrollView.delegate = context.coordinator // Set up delegate
        let hostingController = UIHostingController(rootView: content)
        hostingController.view.translatesAutoresizingMaskIntoConstraints = false
        scrollView.addSubview(hostingController.view)
        NSLayoutConstraint.activate([
            hostingController.view.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
            hostingController.view.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor),
            hostingController.view.topAnchor.constraint(equalTo: scrollView.topAnchor),
            hostingController.view.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
            hostingController.view.heightAnchor.constraint(equalTo: scrollView.heightAnchor)
        ])
        return scrollView
    }
    func updateUIView(_ uiView: UIScrollView, context: Context) {
        // Calculate the visible area of the content
        let visibleRect = CGRect(origin: uiView.contentOffset, size: uiView.bounds.size).insetBy(dx: 0, dy: -40)
        // Only load the elements that are within the visible area
        if let hostingController = uiView.subviews.first as? UIHostingController<Content> {
            let hostingView = hostingController.view!
            for subview in hostingView.subviews {
                if let textView = subview as? Text,
                   !textView.isHidden, !visibleRect.intersects(textView.frame) {
                    textView.isHidden = true
                } else if let textView = subview as? Text,
                   textView.isHidden, visibleRect.intersects(textView.frame) {
                    textView.isHidden = false
                }
            }
        }
        // Load more elements when the user has scrolled to the end of the visible area
        if uiView.contentOffset.y + uiView.bounds.height >= uiView.contentSize.height {
            let totalViews = (content as! LazyHStack<AnyView>).id.count
            let currentPage = Int(uiView.contentSize.height / uiView.bounds.height)
            let nextPage = currentPage + 1
            let startIndex = nextPage * 10 // Load 10 new elements at a time
            let endIndex = min(startIndex + 10, totalViews)
            for index in startIndex..<endIndex {
                if let hostingController = uiView.subviews.first as? UIHostingController<Content> {
                    let hostingView = hostingController.view!
                    let newTextView = Text("Element \(index)")
                    hostingView.addSubview(newTextView)
                    newTextView.frame.origin = CGPoint(x: 200 * (index % 5), y: 50 * (index / 5))
                }
            }
            // Update the content size
            let newContentSize = CGSize(width: uiView.contentSize.width, height: CGFloat(endIndex / 5 + 1) * 50)
            uiView.contentSize = newContentSize
        }
    }
    func makeCoordinator() -> Coordinator {
        Coordinator()
    }
    class Coordinator: NSObject, UIScrollViewDelegate {
        func scrollViewDidScroll(_ scrollView: UIScrollView) {
            scrollView.superview?.setNeedsLayout()
        }
    }
}
struct ContentView: View {
    var body: some View {
        MyLazyHStack {
            LazyHStack(id: \.self) {
                ForEach(1..<100) { index in
                    Text("Element \(index)")
                        .frame(width: 200, height: 40)
                }
            }
        }
    }
}

相关问题