You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI水平ScrollView在iOS15调用scrollTo时出现无规律垂直弹跳问题

问题根因

这个是iOS 15上SwiftUI ScrollView的已知布局bug,根源是iOS 15重构了ScrollView的内容边距计算逻辑,对于未明确固定高度的水平滚动容器,在调用scrollTo触发滚动动画时,系统会错误将内容容器的上下padding值纳入垂直滚动偏移的计算,搭配spring动画的弹性效果就会出现随机的垂直弹跳问题,iOS 14没有这套重构后的布局逻辑,因此不会复现。

你之前用frame修饰符替代padding没有生效,是因为没有明确指定固定的垂直高度,还是让系统自适应内容高度,所以还是会触发边距计算错误。

问题复现效果

ScrollView bug

相关原始代码

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 19:09:04