SwiftUI如何在外层List中嵌入内含List的TabView
问题原因
SwiftUI 里的List本质是对UIKitUITableView的封装,多层List嵌套时系统不会自动给内嵌的滚动视图计算自适应高度,外层List会把内部的TabView、子List都当成普通的单行单元格处理,默认只给极小的布局高度,就会出现内嵌视图被压缩、显示不全的问题,这种嵌套结构完全可以正常实现。
你想用外层List的Section header做Tab选择器吸顶的思路没问题,核心要解决两个坑:
- 内嵌的
TabView没有明确高度约束,拿不到合适的布局尺寸 - 内层
List默认自带独立滚动,会和外层List的滚动冲突,出现双滚动条、滚动错位、吸顶失效的问题
修复方案
- 用
GeometryReader读取外层视图的可用高度,给TabView设置撑满剩余空间的明确高度 - 给内层
List加上.listStyle(.plain)和.scrollDisabled(true),关闭内层独立滚动,所有滚动逻辑交给外层List处理,保留Section吸顶的原生特性 - 把Tab选择器放到
Section的header位置,无需额外自定义逻辑即可实现原生吸顶效果 - 移除List、TabView默认的多余内边距,避免布局错位
修正后的可运行代码:
import SwiftUI struct NestedTabListView: View { @State private var selectedTab = 1 var body: some View { GeometryReader { outerProxy in List { Section { TabView(selection: $selectedTab) { // 第一个Tab内容 Text("First Tab View") .frame(maxWidth: .infinity, maxHeight: .infinity) .tag(1) // 第二个Tab的内嵌List,关闭自身滚动 List { ForEach(0 ..< 20) { index in Text("This is index \(index)") } } .listStyle(.plain) .scrollDisabled(true) .listRowInsets(.init(top: 0, leading: 16, bottom: 0, trailing: 16)) .tag(2) // 第三个Tab内容 Text("Third Tab View") .frame(maxWidth: .infinity, maxHeight: .infinity) .tag(3) } // TabView高度 = 屏幕可用高度 - 安全区高度 - 顶部Tab栏高度,可根据自己的Tab栏高度调整数值 .frame(height: outerProxy.size.height - outerProxy.safeAreaInsets.top - outerProxy.safeAreaInsets.bottom - 44) .tabViewStyle(.page(indexDisplayMode: .never)) .listRowInsets(.init(top: 0, leading: 0, bottom: 0, trailing: 0)) } header: { // 吸顶Tab选择器 HStack { Spacer() Button(action: {selectedTab = 1}) { Text("First Tab") .foregroundColor(selectedTab == 1 ? .accentColor : .gray) } Spacer() Button(action: {selectedTab = 2}) { Text("Second Tab") .foregroundColor(selectedTab == 2 ? .accentColor : .gray) } Spacer() Button(action: {selectedTab = 3}) { Text("Third Tab") .foregroundColor(selectedTab == 3 ? .accentColor : .gray) } Spacer() } .padding(.vertical, 8) .background(.bar) } } .listStyle(.plain) } } }
额外说明
- 如果你的Tab内容高度不固定,可以通过运行时获取内层
UITableView的contentSize动态更新TabView高度,不需要写死数值 - 如果你需要适配iOS16以下系统,
.scrollDisabled(true)可以通过给List写扩展、调用UIKit的isScrollEnabled属性实现同等效果 - 不要在内层List外面额外套一层无意义的
VStack,会干扰系统的高度计算逻辑,加剧布局异常
内容的提问来源于stack exchange,提问作者abcdefg
相关产品推荐
相关产品推荐

