有个需求就是需要获取当前滚动的元素是第几个了,然后把滚动到的元素设置到最中心位置,并且改变这个元素的背景和边框,然后缩放1.5倍。
第一版代码
实现监听滚动到了第几个元素
import SwiftUI
struct ContentView: View {
let items = Array(0 ..< 10)
@State private var currentIndex: Int = 0
var body: some View {
VStack {
Text("当前中心索引: \(currentIndex)")
.font(.title)
.padding()
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 20) {
ForEach(items, id: \.self) { index in
GeometryReader { _ in
Color.blue
.frame(width: 150, height: 150)
.overlay(Text("\(index)").foregroundColor(.white))
.background(GeometryReader { innerProxy in
Color.clear
.onAppear {
updateCurrentIndex(innerProxy: innerProxy, index: index)
}
.onChange(of: innerProxy.frame(in: .global).midX) { oldValue, newValue in
updateCurrentIndex(innerProxy: innerProxy, index: index)
}
})
}
.frame(width: 150, height: 150)
}
}
.padding(.horizontal, (UIScreen.main.bounds.width - 150) / 2)
}
}
}
private func updateCurrentIndex(innerProxy: GeometryProxy, index: Int) {
let screenCenter = UIScreen.main.bounds.width / 2
let itemCenterX = innerProxy.frame(in: .global).midX
let distance = abs(itemCenterX - screenCenter)
DispatchQueue.main.async {
if distance < 85 { // 85 是 150 宽度的一半 + 余量
currentIndex = index
}
}
}
}
struct ScrollViewCenterDetection_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
第二版代码
实现滚动结束后,定位到最后的那个元素,并且增加红色边框
代码如下:
import SwiftUI
struct ContentView: View {
let items = Array(0 ..< 10)
@State private var currentIndex: Int = 0
@State private var timer: Timer?
var body: some View {
VStack {
Text("当前中心索引: \(currentIndex)")
.font(.title)
.padding()
ScrollViewReader { scrollProxy in
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 20) {
ForEach(items, id: \.self) { index in
GeometryReader { _ in
Color.blue
.frame(width: 150, height: 150)
.overlay(Text("\(index)").foregroundColor(.white))
.clipShape(Circle())
.overlay(
Circle()
.stroke(currentIndex == index ? Color.red : Color.clear, lineWidth: 4) // 红色边框
)
.background(GeometryReader { innerProxy in
Color.clear
.onAppear {
updateCurrentIndex(innerProxy: innerProxy, index: index)
}
.onChange(of: innerProxy.frame(in: .global).midX) { _, _ in
updateCurrentIndex(innerProxy: innerProxy, index: index)
}
})
}
.frame(width: 150, height: 150)
.id(index)
}
}
.padding(.horizontal, (UIScreen.main.bounds.width - 150) / 2)
}
.onChange(of: currentIndex) { _, _ in
startTimer {
withAnimation {
scrollProxy.scrollTo(currentIndex, anchor: .center)
}
}
}
}
}
}
private func updateCurrentIndex(innerProxy: GeometryProxy, index: Int) {
let screenCenter = UIScreen.main.bounds.width / 2
let itemCenterX = innerProxy.frame(in: .global).midX
let distance = abs(itemCenterX - screenCenter)
DispatchQueue.main.async {
if distance < 85 { // 85 是 150 宽度的一半 + 余量
currentIndex = index
}
}
}
private func startTimer(completion: @escaping () -> Void) {
timer?.invalidate()
timer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: false) { _ in
completion()
print("done")
}
}
}
struct ScrollViewCenterDetection_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}