SwiftUI中列表滚动指示器内边距异常的修复方法
SwiftUI List垂直滚动指示器顶部内边距问题的原生修复方案(兼容iOS 15)
问题场景
以下是我实现的SwiftUI视图:
struct TestView: View { var body: some View { NavigationView { VStack(alignment: .leading, spacing: 0) { HStack(spacing: 0) { Text("something here") Spacer() } .frame(height: 50) .background(Color.red) List { ForEach(0..<100) { row in HStack { Text("row \(row)") Spacer() } } } .listStyle(.plain) } .navigationTitle("Title") .navigationBarTitleDisplayMode(.inline) } } }
该视图中List的垂直滚动指示器起始位置异常,顶部存在多余内边距。我曾用UIKit编写了一个临时解决方案,但该方案需要遍历视图层级且依赖延迟执行,并非最优解:
.onAppear(perform: { DispatchQueue.main.asyncAfter(deadline: .now() + 0.1, execute: { let window = (UIApplication.shared.connectedScenes.first as? UIWindowScene)?.windows.first let views = window?.recursiveSubviews() let scrollViews = views?.filter({ $0 is UIScrollView }) as? [UIScrollView] scrollViews?.forEach({ $0.verticalScrollIndicatorInsets = .init(top: 1, left: 0, bottom: 0, right: 0) }) }) }) extension UIView { func recursiveSubviews() -> [UIView] { return subviews + subviews.flatMap({ $0.recursiveSubviews() }) } }
原生修复方案(兼容iOS 15)
方案1:精准定位当前List对应的UITableView
通过自定义UIViewRepresentable作为List的背景视图,精准找到当前List对应的UITableView并调整滚动指示器内边距,避免全局遍历视图层级:
struct TestView: View { var body: some View { NavigationView { VStack(alignment: .leading, spacing: 0) { HStack(spacing: 0) { Text("something here") Spacer() } .frame(height: 50) .background(Color.red) List { ForEach(0..<100) { row in HStack { Text("row \(row)") Spacer() } } } .listStyle(.plain) .background(TableViewConfigurator { tableView in // 根据实际需要调整top值 tableView.verticalScrollIndicatorInsets = .init(top: 1, left: 0, bottom: 0, right: 0) }) } .navigationTitle("Title") .navigationBarTitleDisplayMode(.inline) } } } // 自定义视图配置器,用于获取并配置List对应的UITableView struct TableViewConfigurator: UIViewRepresentable { let configure: (UITableView) -> Void func makeUIView(context: Context) -> UIView { let view = UIView() // 延迟执行确保视图层级已构建完成 DispatchQueue.main.async { // 向上查找父级的UITableView(SwiftUI List的底层实现) if let tableView = view.superview?.superview as? UITableView { configure(tableView) } } return view } func updateUIView(_ uiView: UIView, context: Context) {} }
方案2:调整List默认的头部内边距(针对iOS 15)
iOS 15中Plain风格的List默认会添加顶部section头部内边距,这可能导致滚动指示器位置偏移,可通过临时修改全局UITableView的sectionHeaderTopPadding来修复:
struct TestView: View { var body: some View { NavigationView { VStack(alignment: .leading, spacing: 0) { HStack(spacing: 0) { Text("something here") Spacer() } .frame(height: 50) .background(Color.red) List { ForEach(0..<100) { row in HStack { Text("row \(row)") Spacer() } } } .listStyle(.plain) } .navigationTitle("Title") .navigationBarTitleDisplayMode(.inline) .onAppear { // 移除List顶部默认的section头部内边距 UITableView.appearance().sectionHeaderTopPadding = 0 } .onDisappear { // 恢复全局默认值,避免影响其他页面 UITableView.appearance().sectionHeaderTopPadding = UITableView.Style.plain.sectionHeaderTopPadding } } } }
方案说明
- 方案1更精准,仅作用于当前List,不会影响其他视图;
- 方案2操作简单,但属于全局临时修改,需在页面消失时恢复默认值,避免影响其他页面的List布局。
内容的提问来源于stack exchange,提问作者Leszek Szary
相关产品推荐
相关产品推荐

