SwiftUI水平ScrollView在iOS15调用scrollTo时出现无规律垂直弹跳问题
问题根因
这个是iOS 15上SwiftUI ScrollView的已知布局bug,根源是iOS 15重构了ScrollView的内容边距计算逻辑,对于未明确固定高度的水平滚动容器,在调用scrollTo触发滚动动画时,系统会错误将内容容器的上下padding值纳入垂直滚动偏移的计算,搭配spring动画的弹性效果就会出现随机的垂直弹跳问题,iOS 14没有这套重构后的布局逻辑,因此不会复现。
你之前用frame修饰符替代padding没有生效,是因为没有明确指定固定的垂直高度,还是让系统自适应内容高度,所以还是会触发边距计算错误。
问题复现效果

相关原始代码
ScrollView(.horizontal, showsIndicators: false) { ScrollViewReader{ proxy in HStack(spacing: 32){ ForEach(...){ index in QuestionCell(...) .scaleEffect(selectedIndex == index ? 1.175 : 1.0) .onTapGesture{ withAnimation(.spring()){ selectedIndex = index } } } } .padding(.leading) .padding() // Removing this fixes the bounce bug. .onChange(of: selectedIndex) { value in withAnimation(.spring()){ let paramsCount = <SOME MODEL>.count if value < paramsCount{ proxy.scrollTo(value, anchor: .center) }else{ proxy.scrollTo(paramsCount - 1, anchor: .center) } } } } }
可行解决方案
- 方案1:明确固定ScrollView的垂直高度
直接给外层ScrollView设置固定高度,值等于你的cell高度 + 上下总padding值,明确垂直方向的布局约束,避免系统动态计算内容高度时出现偏移错误,示例修改:
// 假设cell高度是80,上下padding各16总高度为112 ScrollView(.horizontal, showsIndicators: false) { // 原有内部代码不变 } .frame(height: 112)
方案2:调整padding的应用位置
不要给HStack直接加上下padding,将上下padding转移到每个QuestionCell的内部实现中,保持整个HStack的高度等于cell的固定高度,这样系统计算滚动偏移时就不会出现垂直方向的误差。方案3:拆分padding修饰符,用frame替代垂直padding
保留水平方向的padding设置,上下的额外间距通过给HStack设置固定高度+垂直居中实现,无需修改ScrollView层级,示例修改:
HStack(spacing: 32){ // 原有ForEach内容不变 } .padding(.leading) .padding(.horizontal) // 只保留水平方向padding .frame(height: 80 + 32, alignment: .center) // 80为cell高度,32为上下总padding .onChange(of: selectedIndex) { value in // 原有逻辑不变 }
内容的提问来源于stack exchange,提问作者Elai
相关产品推荐
相关产品推荐

