SwiftUI结合Slider与Button实现scrollTo滚动定位异常问题排查
修复方案
问题原因梳理
- 滚动先跳顶部的问题:
- 你没有给Section绑定对应的id标识,
scrollTo找不到目标视图就会先滚到列表顶部,再响应后续的定位逻辑;另外你Slider设置的最大值是42,但实际只定义了8-29层的Section,滑动到29以上的不存在的id也会触发跳顶部的问题。 - 列表默认适配导航栏大标题的安全区域,滚动时锚点计算会有偏差,你代码里VStack顶部额外加的Spacer也会干扰布局计算。
- 按钮点击无法滚动的问题:
@State属性修改后不会立刻生效,你修改sliderValue后立即调用scrollTo取到的还是旧值,自然匹配不到目标Section。
修正代码
struct Floors: Identifiable { let id: String let name: String } struct UnitLine: Identifiable { let id = UUID() let name: String } struct UnitKeyboardView: View { @Environment(\.presentationMode) var presentationMode @State private var sliderValue = 8.0 var body: some View { let floors = [Floors(id:"8",name:"8"),Floors(id:"9",name:"9"),Floors(id:"10",name:"10"),Floors(id:"11",name:"11"),Floors(id:"12",name:"12"),Floors(id:"13",name:"13"),Floors(id:"14",name:"14"),Floors(id:"15",name:"15"),Floors(id:"16",name:"16"),Floors(id:"17",name:"17"),Floors(id:"18",name:"18"),Floors(id:"19",name:"19"),Floors(id:"20",name:"20"),Floors(id:"21",name:"21"),Floors(id:"22",name:"22"),Floors(id:"23",name:"23"),Floors(id:"24",name:"24"),Floors(id:"25",name:"25"),Floors(id:"26",name:"26"),Floors(id:"27",name:"27"),Floors(id:"28",name:"28"),Floors(id:"29",name:"29")] let unitlines = [UnitLine(name:"01"),UnitLine(name:"02"),UnitLine(name:"03"),UnitLine(name:"04"),UnitLine(name:"05"),UnitLine(name:"06"),UnitLine(name:"07"),UnitLine(name:"08")] NavigationView { ScrollViewReader { proxy in VStack(spacing: 0) { List { ForEach(floors) { i in Section(header: Text("Floor \(i.name)")) { ForEach(unitlines) { u in Text(u.name) .padding(.trailing,200) .contentShape(Rectangle()) .onTapGesture { print("tapped") presentationMode.wrappedValue.dismiss() } } } .id(i.id) // 给Section绑定id,供scrollTo定位 } } .listStyle(.grouped) // 显式指定列表样式,避免系统默认样式差异导致的布局跳动 HStack { Spacer() Text("Flr: \(Int(sliderValue))") .font(.title) .padding(10) Button { if sliderValue > 8 { let newValue = sliderValue - 1.0 sliderValue = newValue // 直接用计算好的新值定位,不用等State更新 proxy.scrollTo(String(Int(newValue)), anchor: .topLeading) } } label: { Image(systemName: "minus") } .frame(width:30, height:15) .padding(10) .background(Color.red) .clipShape(Capsule()) Slider(value: $sliderValue, in: 8...29, step: 1.0, onEditingChanged: { _ in // 最大值改为29,和实际楼层数量匹配,避免滚动到不存在的id withAnimation { proxy.scrollTo(String(Int(sliderValue)), anchor: .topLeading) } }) .background(Color.cyan) .border(Color.blue, width: 1) .padding(10) Button { if sliderValue < 29 { let newValue = sliderValue + 1.0 sliderValue = newValue // 直接用计算好的新值定位 proxy.scrollTo(String(Int(newValue)), anchor: .topLeading) } } label: { Image(systemName: "plus") } .frame(width:30, height:15) .padding(10) .background(Color.green) .clipShape(Capsule()) Spacer() } .background(Color.gray) } } .navigationTitle("Select Address") // 如果不需要大标题,可加下面这行彻底避免导航栏高度变化导致的跳动 // .navigationBarTitleDisplayMode(.inline) } } }
内容的提问来源于stack exchange,提问作者Dave
相关产品推荐
相关产品推荐

