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

SwiftUI中TabView的List在真机显示于TabBar下方,模拟器却正常?

问题

运行下述Swift代码时,List会显示在TabView创建的TabBar后方;但注释掉Image组件后,该问题便不再出现。请问如何确保List不会显示在TabBar下方?

struct MainView: View {
    var body: some View {
        TabView {
             ScheduleView()
                 .tabItem { Label ("Schedule", systemImage: "calendar") }
             ScheduleView()
                 .tabItem { Label ("Schedule", systemImage: "calendar") }
             ScheduleView()
                 .tabItem { Label ("Schedule", systemImage: "calendar") }
    
        
        }
    }
}

struct ScheduleView: View {
    @State var category = Categories.mixed
    
    var body: some View {
        VStack {
            HStack(alignment: .top) {
                Image("svrge-logo")
                    .resizable()
                    .scaledToFit()
                    .frame(height: 80)
                    .padding(.leading)
                Spacer()
                VStack(alignment: .trailing) {
                    Text ("Ver: 1.0")
                    Text ("nn matches")
                }
                .padding(.horizontal)
                .font(.caption)
            }
            
            Picker ("Gender", selection: $category) {
                ForEach(Categories.allCases) {
                    Text ($0.id).tag($0)
                }
            }
            .pickerStyle(SegmentedPickerStyle())
            
            List(filteredMatches(for: category)) { match in
                VStack {
                    HStack {
                        Text (match.matchNumber)
                        Spacer()
                        Text (match.date.toString(withFormat: "dd.MM.yyyy @ HH:mm"))
                    }
                    HStack {
                        Text (match.homeTeamName)
                        Spacer()
                        Text (match.awayTeamName)
                    }
                }
                
            }
            .listStyle(.plain)
        }
    }
}

enum Categories: String, Codable, CaseIterable, Identifiable {
    case womens
    case mens
    case mixed
    
    var id: String {
        return self.rawValue.capitalized
    }
}

var allMatches: [MatchInfo] = MatchInfo.data

func filteredMatches(for category: Categories) -> [MatchInfo] {
    return allMatches
        .filter { $0.category == category }
        .sorted()
}

let teamNames1 = (0...30).map{"Team \($0)"}
let teamNames2 = (31...60).map{"Team \($0)"}

struct MatchInfo: Identifiable, Comparable {
    var id: String { "\(season)-\(matchNumber)" }
    
    var season = "2022-2023"
    var matchNumber: String
    var date: Date = Date()

    var location = "Some Gym"
    var category:Categories = Categories.allCases.randomElement()!
    
    var homeTeamName = teamNames1.randomElement()!
    var awayTeamName = teamNames2.randomElement()!
    
    static func < (lhs: MatchInfo, rhs: MatchInfo) -> Bool {
        return lhs.date.timeIntervalSince(rhs.date) < 0
    }
    
    static func == (lhs: MatchInfo, rhs: MatchInfo) -> Bool {
        return lhs.date == rhs.date
    }
}
解决方案

这个问题是因为添加Image后,VStack布局没有正确适配屏幕安全区域,导致List超出TabBar所在的底部安全区。可以通过以下两种方式修复:

方法一:强制List占据剩余可用空间

给List添加frame(maxHeight: .infinity),让它只填充TabBar上方的剩余空间;同时给外层VStack设置edgesIgnoringSafeArea(.top),允许顶部内容延伸到状态栏区域,避免顶部留白:

修改后的ScheduleView代码:

struct ScheduleView: View {
    @State var category = Categories.mixed
    
    var body: some View {
        VStack {
            HStack(alignment: .top) {
                Image("svrge-logo")
                    .resizable()
                    .scaledToFit()
                    .frame(height: 80)
                    .padding(.leading)
                Spacer()
                VStack(alignment: .trailing) {
                    Text ("Ver: 1.0")
                    Text ("nn matches")
                }
                .padding(.horizontal)
                .font(.caption)
            }
            .padding(.top) // 可选:给顶部栏加一点内边距更美观
            
            Picker ("Gender", selection: $category) {
                ForEach(Categories.allCases) {
                    Text ($0.id).tag($0)
                }
            }
            .pickerStyle(SegmentedPickerStyle())
            .padding(.horizontal) // 可选:给Picker加左右内边距
            
            List(filteredMatches(for: category)) { match in
                VStack {
                    HStack {
                        Text (match.matchNumber)
                        Spacer()
                        Text (match.date.toString(withFormat: "dd.MM.yyyy @ HH:mm"))
                    }
                    HStack {
                        Text (match.homeTeamName)
                        Spacer()
                        Text (match.awayTeamName)
                    }
                }
            }
            .listStyle(.plain)
            .frame(maxHeight: .infinity) // 关键:让List只占TabBar上方的剩余空间
        }
        .edgesIgnoringSafeArea(.top) // 仅忽略顶部安全区,底部自动避开TabBar
    }
}

方法二:用NavigationStack包裹内容(适合需要导航的场景)

如果页面需要导航栏,直接用NavigationStack包裹整个VStack,系统会自动处理安全区域与TabBar的布局冲突:

struct ScheduleView: View {
    @State var category = Categories.mixed
    
    var body: some View {
        NavigationStack {
            VStack {
                // 原HStack、Picker、List代码保持不变
            }
            .navigationTitle("Schedule") // 可选设置导航标题
        }
    }
}

核心原理

添加Image后,VStack的总高度可能超过了屏幕的可用显示区域(包含安全区),List默认会试图填充所有空间,导致部分内容延伸到TabBar下方。通过给List设置maxHeight: .infinity,可以约束它的高度范围;edgesIgnoringSafeArea(.top)则是优化顶部布局,避免出现不必要的留白。


内容的提问来源于stack exchange,提问作者Zonker.in.Geneva

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 04:17:47