在SwiftUI中,控件事件在哪里,例如,scrollViewDidScroll用于检测列表数据的底部

J. *_*ell 7 swift swiftui

在SwiftUI中,是否有人知道控制事件(例如:scrollViewDidScroll)在哪里,以检测用户何时到达列表的底部,从而导致事件检索其他数据块?还是有一种新的方法来做到这一点?

好像UIRefreshControl()也不存在...

Mat*_*ini 13

SwiftUI缺少许多功能- 目前似乎无法实现

但是,这是一种解决方法

TL; DR直接跳过答案的底部

一个有趣的发现,同时在ScrollView和之间进行了一些比较List

struct ContentView: View {

    var body: some View {

        ScrollView {
            ForEach(1...100) { item in
                Text("\(item)")
            }
            Rectangle()
                .onAppear { print("Reached end of scroll view")  }
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

我在a内Rectangle100个Text项目的末尾附加了ScrollView一个printin onDidAppear

ScrollView即使出现前20件物品,它也会在出现时触发。

Scrollview内部的所有视图都将立即呈现,即使它们不在屏幕上也是如此。

我尝试使用进行相同操作List,但行为有所不同。

struct ContentView: View {

    var body: some View {

        List {
            ForEach(1...100) { item in
                Text("\(item)")
            }
            Rectangle()
                .onAppear { print("Reached end of scroll view")  }
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

The print gets executed only when the bottom of the List is reached!

So this is a temporary solution, until SwiftUI API gets better.

Use a List and place a "fake" view at the end of it, and put fetching logic inside onAppear { }

  • “这可能意味着 Scrollview 内的所有视图都会立即渲染”...使用视图调试器,您将看到这正是发生的情况。这是相当标准的滚动视图模式,您可以在其中添加所有内容(然后可以从中确认“contentSize”并显示适当的滚动指示器)。 (2认同)

Vic*_*rov 11

您可以检查最新元素是否出现在 onAppear 中。

struct ContentView: View {
    @State var items = Array(1...30)

    var body: some View {
        List {
            ForEach(items, id: \.self) { item in
                Text("\(item)")
                .onAppear {
                    if let last == self.items.last {
                        print("last item")
                        self.items += last+1...last+30
                    }
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


Men*_*nno 10

如果您需要有关滚动视图或列表如何滚动的更精确信息,您可以使用以下扩展作为解决方法:

extension View {

    func onFrameChange(_ frameHandler: @escaping (CGRect)->(), 
                    enabled isEnabled: Bool = true) -> some View {

        guard isEnabled else { return AnyView(self) }

        return AnyView(self.background(GeometryReader { (geometry: GeometryProxy) in

            Color.clear.beforeReturn {

                frameHandler(geometry.frame(in: .global))
            }
        }))
    }

    private func beforeReturn(_ onBeforeReturn: ()->()) -> Self {
        onBeforeReturn()
        return self
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以像这样利用更改后的框架的方式:

struct ContentView: View {

    var body: some View {

        ScrollView {

            ForEach(0..<100) { number in

                Text("\(number)").onFrameChange({ (frame) in

                    print("Origin is now \(frame.origin)")

                }, enabled: number == 0)
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

onFrameChange滚动时将调用闭包。使用与透明颜色不同的颜色可能会带来更好的性能。

编辑:我通过将框架置于闭包之外对代码进行了一些改进beforeReturn。这有助于在该闭包中几何代理不可用的情况下。