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

SwiftUI模态Sheet底部出现多余空白的解决方法咨询

问题描述

我开发了一个包含2个标签页的基础TabView,其中Tab 2的按钮可唤起嵌入在NavigationStack内、带有工具栏的Page Style TabView模态Sheet。添加NavigationStack后,Sheet页面底部出现了多余空白区域。我已尝试在多个位置使用.ignoreSafeArea(edges: .bottom),但问题仍未解决。请问我遗漏了什么?如何消除该多余空白?NavigationStack的位置是否有误?

开发环境:iOS 16.1、Xcode 14.2

代码如下:

struct HomeView: View {

  @State private var pageIndex = 0
    var body: some View {
      TabView(selection: $pageIndex) {
        PageOne()
          .tabItem {
            Label("Page 1", systemImage: "star")
          }.tag(0)
        PageTwo()
          .tabItem {
            Label("Page 2", systemImage: "bookmark")
          }.tag(1)
      }
    }
}

struct PageOne: View {
  var body: some View {
    VStack {
      Text("Page 1")
    }
  }
}

struct PageTwo: View {
  @State private var sheetIsShowing = false
  var body: some View {
    VStack {
      Text("Page 2")
      Button("Show Sheet") {
        self.sheetIsShowing.toggle()
      }
    }
    .fullScreenCover(isPresented: $sheetIsShowing) {
      SheetView(sheetIsShowing: $sheetIsShowing)
    }
  }
}

struct SheetView: View {

  @Binding var sheetIsShowing: Bool

  var body: some View {

    NavigationStack {
      TabView {
        SheetTabView1()
        SheetTabView2()
      }
      .tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
      .toolbar {
        ToolbarItem(placement: .navigationBarLeading) {
          Button {
            self.sheetIsShowing.toggle()
          } label: {
            Text("Cancel")
          }
        }
      }
    }
    .ignoresSafeArea(edges: .bottom)
  }
}

struct SheetTabView1: View {
  @State private var text1 = ""
  @State private var text2 = ""
  var body: some View {
    List {
      TextField("Enter Text", text: $text1)
      TextField("Enter Text", text: $text2)
    }
  }
}

struct SheetTabView2: View {
  @State private var text1 = ""
  @State private var text2 = ""
  var body: some View {
    List {
      TextField("Enter Text", text: $text1)
      TextField("Enter Text", text: $text2)
    }
  }
}

问题截图:
Sheet底部多余空白

解决方法

这个问题的核心是NavigationStack与TabView的层级冲突,以及List的安全区域适配逻辑,调整层级并针对性设置即可解决:

  • 调整NavigationStack的嵌套层级:将NavigationStack从包裹整个TabView,改为嵌套在每个Sheet子页面(SheetTabView1、SheetTabView2)内部。PageTabViewStyle的TabView本身会处理页面布局,外层套NavigationStack会导致系统安全区域计算混乱。
  • 给List添加底部安全区域忽略:在每个Sheet子页面的List上添加.ignoresSafeArea(edges: .bottom),让List直接填充底部空白区域。
  • 移除无效的全局ignoreSafeArea:删除SheetView中加在NavigationStack外层的.ignoresSafeArea(edges: .bottom),该设置在新层级下无效。

修改后的完整代码:

struct HomeView: View {

  @State private var pageIndex = 0
    var body: some View {
      TabView(selection: $pageIndex) {
        PageOne()
          .tabItem {
            Label("Page 1", systemImage: "star")
          }.tag(0)
        PageTwo()
          .tabItem {
            Label("Page 2", systemImage: "bookmark")
          }.tag(1)
      }
    }
}

struct PageOne: View {
  var body: some View {
    VStack {
      Text("Page 1")
    }
  }
}

struct PageTwo: View {
  @State private var sheetIsShowing = false
  var body: some View {
    VStack {
      Text("Page 2")
      Button("Show Sheet") {
        self.sheetIsShowing.toggle()
      }
    }
    .fullScreenCover(isPresented: $sheetIsShowing) {
      SheetView(sheetIsShowing: $sheetIsShowing)
    }
  }
}

struct SheetView: View {

  @Binding var sheetIsShowing: Bool

  var body: some View {
    TabView {
      SheetTabView1()
      SheetTabView2()
    }
    .tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
    .toolbar {
      ToolbarItem(placement: .navigationBarLeading) {
        Button {
          self.sheetIsShowing.toggle()
        } label: {
          Text("Cancel")
        }
      }
    }
  }
}

struct SheetTabView1: View {
  @State private var text1 = ""
  @State private var text2 = ""
  var body: some View {
    NavigationStack {
      List {
        TextField("Enter Text", text: $text1)
        TextField("Enter Text", text: $text2)
      }
      .ignoresSafeArea(edges: .bottom)
    }
  }
}

struct SheetTabView2: View {
  @State private var text1 = ""
  @State private var text2 = ""
  var body: some View {
    NavigationStack {
      List {
        TextField("Enter Text", text: $text1)
        TextField("Enter Text", text: $text2)
      }
      .ignoresSafeArea(edges: .bottom)
    }
  }
}

补充:如果不需要每个子页面独立的导航逻辑,也可以将NavigationStack放在TabView内部(作为TabView的直接父视图),同时给TabView添加.ignoresSafeArea(edges: .bottom),但前者的结构更灵活,便于后续扩展每个页面的导航功能。

内容的提问来源于stack exchange,提问作者sandPitPilot

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 12:01:08