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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 08:06:06