1、代码如下:
import SwiftUI
let kWidth = UIScreen.main.bounds.width
struct SPScrollTestView: View {
@State private var offset: CGFloat = 0.0
/// 上一次的offset,判断滚动方向
@State private var lastOffset: CGFloat = 0.0
/// 滚动状态
@State private var isScrolling: Bool = false
/// 是否往上滑动
@State private var toTop: Bool = true
/// 是否可以自动滚动
@State private var canScroll: Bool = false
var body: some View {
GeometryReader { proxy in
ScrollViewReader(content: { scrollProxy in
ScrollView(.vertical) {
GeometryReader { geometry in
Color.clear.preference(key: ViewOffsetKey.self, value: geometry.frame(in: .named("scrollView")).minY)
}.frame(height: 0)
LazyVStack(spacing:10) {
ForEach(0..<10) { i in
GeometryReader { reader in
ZStack {
Rectangle()
.fill(Color.yellow)
Text("Page: \(i)")
.bold()
}
.frame(width: kWidth,height: 300 + CGFloat(50 * i))
.cornerRadius(5)
.shadow(radius: 5)
.id("scroll\(i)")
.onChange(of: canScroll) { _ in
if canScroll {
let minY = reader.frame(in: .global).minY
// let midY = reader.frame(in: .global).midY
let maxY = reader.frame(in: .global).maxY
let height = reader.size.height
if toTop {
if minY < 0 ,maxY > 0 {
print("========================【上】================================")
if abs(minY) > height / 3 {
withAnimation {
scrollProxy.scrollTo("scroll\(i+1)", anchor: .top)
}
} else {
withAnimation {
scrollProxy.scrollTo("scroll\(i)", anchor: .top)
}
}
}
} else {
if minY < offset,maxY < height,maxY > 0 {
if maxY > height / 4 {
withAnimation {
scrollProxy.scrollTo("scroll\(i)", anchor: .top)
}
} else {
withAnimation {
scrollProxy.scrollTo("scroll\(i+1)", anchor: .top)
}
}
}
}
}
}
}.frame(width: kWidth,height: 300 + CGFloat(50 * i))
}
}
}
.coordinateSpace(name:"scrollView")
.onChange(of: isScrolling) { _ in
canScroll = !isScrolling
if isScrolling == false {
if lastOffset < offset { // 往上
toTop = true
} else {
toTop = false
}
lastOffset = offset
} else {
lastOffset = offset
}
}
.onPreferenceChange(ViewOffsetKey.self) { offset in
if offset > 0 { return }
self.offset = -offset
}
.scrollStatusMonitor($isScrolling, monitorMode: .exclusion)
})
}.ignoresSafeArea()
}
}
#Preview {
SPScrollTestView()
}
//计算scroll 偏移量
struct ViewOffsetKey: PreferenceKey {
typealias Value = CGFloat
static var defaultValue: CGFloat = 0
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
value += nextValue()
}
}