我创建了一个 View 扩展来读取它的偏移量(灵感来自https://fivestars.blog/swiftui/swiftui-share-layout-information.html):
func readOffset(in coordinateSpace: String? = nil, onChange: @escaping (CGFloat) -> Void) -> some View {
background(
GeometryReader {
Color.clear.preference(key: ViewOffsetKey.self,
value: -$0.frame(in: coordinateSpace == nil ? .global : .named(coordinateSpace)).origin.y)
})
.onPreferenceChange(ViewOffsetKey.self, perform: onChange)
}
我也在使用 Federico 的 readSize 函数:
func readSize(onChange: @escaping (CGSize) -> Void) -> some View {
background(
GeometryReader { geo in
Color.clear
.preference(key: SizePreferenceKey.self, value: geo.size)
})
.onPreferenceChange(SizePreferenceKey.self, perform: onChange)
}
两者一起帮助我确定滚动视图中的子视图是否在屏幕上/在屏幕外:
struct TestInfinityList: View {
@State var visibleItems: Set<Int> = []
@State var items: [Int] = Array(0...20)
@State var size: CGSize = .zero
var body: some View {
ScrollView(.vertical) {
ForEach(items, id: \.self) { item in
GeometryReader { geo in
VStack {
Text("Item \(item)")
}.id(item)
.readOffset(in: "scroll") { newOffset in
if !isOffscreen(when: newOffset, in: size.height) {
visibleItems.insert(item)
}
else {
visibleItems.remove(item)
}
}
}.frame(height: 300)
}
}.coordinateSpace(name: "scroll")
}
.readSize { newSize in
self.size = newSize
}
}
这是检查可见性的 isOffscreen 函数:
func isOffscreen(when offset: CGFloat, in height: CGFloat) -> Bool {
if offset <= 0 && offset + height >= 0 {
return false
}
return true
}
一切正常。但是,我想将代码进一步优化为一个单独的扩展,该扩展根据输入的偏移量和 size.height 检查可见性,并且还接收关于可见时做什么的参数,即移动 readOffset 的闭包成为逻辑与扩展代码共存。
我不知道这是否可行,但认为值得一问。