SwiftUI中如何让ScrollView内图片居中时触发.scaleEffect动画?
SwiftUI横屏水平ScrollView实现中央图片放大动画
要实现滚动时屏幕中央的图片自动放大,核心是动态计算每个图片与屏幕中心的距离,根据距离调整缩放比例,结合SwiftUI的偏好设置和几何读取器就能完成。以下是具体实现方案:
实现步骤
- 用
PreferenceKey跟踪ScrollView的滚动偏移量,实时获取滚动位置 - 为每个图片添加
GeometryReader,获取其在全局坐标系中的位置 - 计算图片中心与屏幕中心的水平距离,根据距离动态生成缩放系数(距离越近,缩放比例越大)
- 给缩放效果添加平滑动画,让过渡更自然
修改后的完整代码
import SwiftUI // 用于跟踪ScrollView滚动偏移的偏好键 struct ScrollOffsetPreferenceKey: PreferenceKey { static var defaultValue: CGFloat = 0 static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { value = nextValue() } } struct TestView: View { // 存储滚动偏移量 @State private var scrollOffset: CGFloat = 0 var body: some View { GeometryReader { screenGeometry in let screenCenterX = screenGeometry.size.width / 2 // 图片基础宽度 let imageBaseWidth = screenGeometry.size.width / 3 // 触发缩放变化的最大距离(图片宽度+间距的一半) let maxScaleDistance = (imageBaseWidth + 20) / 2 VStack { Spacer() ScrollView(.horizontal, showsIndicators: false) { ScrollViewReader { proxy in HStack(spacing: 20) { ForEach(1...6, id: \.self) { index in Image("img\(index)") .resizable() .aspectRatio(contentMode: .fit) .frame(width: imageBaseWidth) .id(index) // 获取图片的全局位置 .background( GeometryReader { imageProxy in Color.clear.preference( key: ImagePositionPreferenceKey.self, value: [index: imageProxy.frame(in: .global).midX] ) } ) // 根据位置计算缩放比例 .scaleEffect(getScale(for: index, screenCenterX: screenCenterX, maxScaleDistance: maxScaleDistance)) .animation(.easeInOut(duration: 0.2), value: scrollOffset) } } } } .coordinateSpace(name: "scroll") // 跟踪ScrollView的滚动偏移 .background( GeometryReader { scrollProxy in Color.clear.preference( key: ScrollOffsetPreferenceKey.self, value: scrollProxy.frame(in: .named("scroll")).minX ) } ) .onPreferenceChange(ScrollOffsetPreferenceKey.self) { newValue in scrollOffset = newValue } Spacer() } } } // 存储每个图片的中心位置 private struct ImagePositionPreferenceKey: PreferenceKey { static var defaultValue: [Int: CGFloat] = [:] static func reduce(value: inout [Int: CGFloat], nextValue: () -> [Int: CGFloat]) { value.merge(nextValue()) { $1 } } } // 计算指定图片的缩放比例 private func getScale(for index: Int, screenCenterX: CGFloat, maxScaleDistance: CGFloat) -> CGFloat { guard let imageCenterX = ImagePositionPreferenceKey.defaultValue[index] else { return 1.0 } let distance = abs(imageCenterX - screenCenterX) // 距离在阈值内时,线性计算缩放比例(1.0到1.2之间) if distance < maxScaleDistance { return 1.0 + (0.2 * (1 - distance / maxScaleDistance)) } else { return 1.0 } } } struct TestView_Previews: PreviewProvider { static var previews: some View { TestView() .previewInterfaceOrientation(.landscapeLeft) } }
关键代码说明
- ScrollOffsetPreferenceKey:用来捕获ScrollView的滚动偏移,实时更新
scrollOffset变量 - ImagePositionPreferenceKey:记录每个图片的全局中心位置,为计算距离提供数据
- getScale方法:根据图片中心与屏幕中心的距离,动态生成缩放比例——距离为0(完全居中)时缩放至1.2,超过阈值时恢复原大小
- 动画设置:给
scaleEffect绑定scrollOffset作为动画触发值,让缩放过渡更平滑
内容的提问来源于stack exchange,提问作者Pro Girl
相关产品推荐
相关产品推荐

