【问题标题】:SwiftUI Detect bottom while scrollingSwiftUI 在滚动时检测底部
【发布时间】:2020-10-11 12:45:19
【问题描述】:

我已经在 SwiftUI 中使用 UIViewRepresentable 实现了我的自定义 UIScrollView,我想检测列表的底部,以便加载下一批数据。我找到了一些答案,但它们似乎不适用于我的UIViewRepresentable

我已经在我的协调器中实现了这个,但是当我到达底部时没有任何反应。

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    if (scrollView.contentOffset.y + 1) >= (scrollView.contentSize.height - scrollView.frame.size.height) {
        print("bottom reached")
        // not working
    }
}

问题可能出在我的 Swift 视图中,而不是在 UIViewRepresentable.?

struct CustomScrollView<Content: View, VM: LoadProtocol> : UIViewRepresentable {
    var width : CGFloat
    var height : CGFloat
    
    var viewModel: VM
    let content: () -> Content
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self, viewModel: viewModel)
    }
    
    func makeUIView(context: Context) -> UIScrollView {
        let control = UIScrollView()
        control.refreshControl = UIRefreshControl()
        control.refreshControl?.addTarget(context.coordinator, action: #selector(Coordinator.handleRefreshControl), for: .valueChanged)
        
        let childView = UIHostingController(rootView: content())
        
        childView.view.frame = CGRect(x: 0, y: 0, width: width, height: height)
        
        control.addSubview(childView.view)
        return control
    }
    
    func updateUIView(_ uiView: UIScrollView, context: Context) { }
    
    class Coordinator: NSObject {
        var control: CustomScrollView<Content, VM>
        var viewModel: VM
        init(_ control: CustomScrollView, viewModel: VM) {
            self.control = control
            self.viewModel = viewModel
        }
        
        func scrollViewDidScroll(_ scrollView: UIScrollView) {
            if (scrollView.contentOffset.y + 1) >= (scrollView.contentSize.height - scrollView.frame.size.height) {
                print("bottom reached")
                // not working
            }
        }
        
        @objc func handleRefreshControl(sender: UIRefreshControl) {
            sender.endRefreshing()
            viewModel.refresh()
        }
    }
}
    
    
    struct SeeAllView: View {
        @ObservedObject var seeAllViewModel: SeeAllViewModel
        
        var body: some View {
            VStack {
                GeometryReader { geometry in
                    CustomScrollView(width: geometry.size.width, height: geometry.size.height, viewModel: self.seeAllViewModel) {
                        SeeAllListView(seeAllViewModel: self.seeAllViewModel)
                    }
                }
            }
        }
    }

【问题讨论】:

    标签: ios swift uiscrollview swiftui


    【解决方案1】:

    您没有在makeUIView 中设置委托。以下更改将修复您的代码 - 我删除了一些缺少的依赖项以使代码编译。它在 Playground 中按预期工作。

    import SwiftUI
    import PlaygroundSupport
    
    struct CustomScrollView<Content: View> : UIViewRepresentable {
        var width : CGFloat
        var height : CGFloat
        
        let content: () -> Content
        
        func makeCoordinator() -> Coordinator {
            Coordinator(self)
        }
        
        func makeUIView(context: Context) -> UIScrollView {
            let control = UIScrollView()
            control.refreshControl = UIRefreshControl()
            control.refreshControl?.addTarget(context.coordinator, action: #selector(Coordinator.handleRefreshControl), for: .valueChanged)
            control.delegate = context.coordinator
            
            let childView = UIHostingController(rootView: content())
            childView.view.frame = CGRect(x: 0, y: 0, width: width, height: height)
            
            control.addSubview(childView.view)
            return control
        }
        
        func updateUIView(_ uiView: UIScrollView, context: Context) { }
        
        class Coordinator: NSObject, UIScrollViewDelegate {
            var control: CustomScrollView<Content>
            init(_ control: CustomScrollView) {
                self.control = control
            }
            
            func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
                if (scrollView.contentOffset.y + 1) >= (scrollView.contentSize.height - scrollView.frame.size.height) {
                    print("bottom reached")
                    // not working
                }
            }
            
            @objc func handleRefreshControl(sender: UIRefreshControl) {
                sender.endRefreshing()
            }
        }
    }
        
        
    struct SeeAllView: View {
        
        var body: some View {
            VStack {
                GeometryReader { geometry in
                    CustomScrollView(width: geometry.size.width, height: geometry.size.height) {
                        List {
                            Text("1")
                            Text("2")
                            Text("3")
                        }
                    }
                }
            }
        }
    }
    
    PlaygroundPage.current.setLiveView(SeeAllView())
    

    【讨论】:

    • 现在它的打印但是在我滚动到底部后我需要强制向上滚动它,然后它打印到底部达到 20 次。这正常吗?
    • 使用scrollViewDidEndDecelerating 而不是scrollViewDidScroll。那应该解决它。我还更新了答案以反映这一点。
    • 谢谢,最后一件事,我怎么能不完全在底部而是在之前触发它呢?你知道大多数应用程序都在这样做。让我们说底部前 100 像素?
    猜你喜欢
    • 2020-11-01
    • 2012-01-03
    • 1970-01-01
    • 1970-01-01
    • 2012-09-25
    • 1970-01-01
    • 2020-02-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多