SwiftUI滚动时Section内VStack伸缩导致列表滚动卡顿
SwiftUI iOS13+ 列表滚动卡顿问题排查
问题背景
我正在开发一款集成按钮及对应操作选项的简易测试应用,为保证向后兼容性,项目最低支持版本设定为iOS 13.0。
预期实现和iOS原生应用一致的流畅滚动效果,但实际运行时列表滚动出现卡顿。我判断卡顿原因是部分VStack持续收缩、扩张,不断改变滚动内容的整体高度,进而引发滚动跳变。
问题复现视频:卡顿复现录像。当滚动到卡顿的底部位置停止滑动时,可以观察到处于收缩状态的VStack,该现象在视频末尾清晰可见。
相关代码
主视图实现代码(初始怀疑异常出现在SectionH)
import SwiftUI struct ContentView: View { @Environment(\.verticalSizeClass) var verticalSizeClass: UserInterfaceSizeClass? @Environment(\.horizontalSizeClass) var horizontalSizeClass: UserInterfaceSizeClass? var body: some View { if(verticalSizeClass == .regular && horizontalSizeClass == .compact) { VStack { List { SectionA() SectionB() SectionC() SectionD() SectionE() SectionF() SectionG() SectionH() } OtherView() } } else { HStack { List { SectionA() SectionB() SectionC() SectionD() SectionE() SectionF() SectionG() SectionH() } OtherView() } } } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }
普通Section示例(SectionA到SectionG实现逻辑类似,以SectionF为例)
import SwiftUI struct SectionF: View { @State var bool1 : Bool = true; @State var text1: String = "text" @State var text2: String = "text" @State var text3: String = "text" @State var text4: String = "text" var body: some View { Section(header: Text("Section F")) { Button{ } label: { Text("button") } VStack(alignment: .leading) { Button{ } label: { Text("button: \(bool1 ? "true" : "false")") } Toggle("bool", isOn: $bool1) } VStack(alignment: .leading) { Button{ } label: { Text("button") } HStack { Text("text: ") TextField("text", text: $text1) } HStack { Text("text: ") TextField("text", text: $text2) } HStack { Text("text: ") TextField("text", text: $text3) } HStack { Text("text: ") TextField("text", text: $text4) } } VStack(alignment: .leading) { Button{ } label: { Text("button") } HStack { Text("text: ") TextField("text", text: $text1) } } } } } struct SectionF_Previews: PreviewProvider { static var previews: some View { Form { SectionF() } } }
初始怀疑引发问题的SectionH实现代码
import SwiftUI struct SectionH: View { @State var text1: String = "text" @State var text2: String = "text" @State var picker1: String = "1" var picker1s = ["1", "2"] @State var text3: String = "text" @State var text4: String = "text" @State var bool1 : Bool = true; @State var text5: String = "text" @State var bool2 : Bool = true; @State var bool3 : Bool = true; @State var number1: String = "0" var body: some View { Section(header: Text("Section H")) { VStack(alignment: .leading) { Button{ } label: { Text("button") } HStack { Text("text: ") TextField("text", text: $text1) } HStack { Text("text: ") TextField("text", text: $text2) } VStack(alignment: .leading) { Text("picker: \(picker1)") Picker("picker", selection: $picker1) { ForEach(picker1s, id: \.self) { Text($0) } }.pickerStyle(.segmented) } HStack { Text("text: ") TextField("text", text: $text3) } HStack { Text("text: ") TextField("text", text: $text4) } } Button { } label: { Text("Button") } VStack(alignment: .leading) { Button { } label: { Text("Button") } VStack { HStack { Text("text: ") TextField("text", text: $text1) } HStack { Text("text: ") TextField("text", text: $text2) } VStack(alignment: .leading) { Text("picker: \(picker1)") Picker("picker", selection: $picker1) { ForEach(picker1s, id: \.self) { Text($0) } }.pickerStyle(.segmented) } } //**this is shrinking VStack!** VStack { Toggle("bool", isOn: $bool1) HStack { Text("text: ") TextField("text", text: $text3) } HStack { Text("text: ") TextField("text", text: $text5) } Toggle("bool", isOn: $bool2) Toggle("bool", isOn: $bool3) HStack { Text("number: ") TextField("number", text: $number1) } } } } } } struct SectionH_Previews: PreviewProvider { static var previews: some View { Form { SectionH() } } }
已尝试排查方案
- 将List替换为Form
- 调整各个Section的排列顺序
- 移除根据设备方向切换VStack/HStack布局的判断逻辑:该操作可以完全消除卡顿
- 精简SectionH内的控件数量
- 将SectionH内的选项拆分到两个独立VStack中(该修改已应用到上述给出的代码中)
问题更新
- 移除SectionH后,卡顿问题仍未解决
- 移除OtherView后,卡顿问题仍未解决
- 目前推测问题和设备方向相关的状态判断逻辑存在关联
我是Swift和SwiftUI的初学者,不清楚该问题的触发原理,也未找到明确的排查解决方向,在此寻求帮助。
内容的提问来源于stack exchange,提问作者Ggumdori
相关产品推荐
相关产品推荐

