Sheet

SwiftUI 中,.sheet 是一个视图修饰符,用于展示一个模态弹窗(Modal Presentation)。它的特点是与某个视图绑定,用于呈现一个子视图。以下是关于 .sheet 的使用规范和它可以添加在哪些视图下面的详细解答。


.sheet 可以加在哪些视图下面? #

.sheet 可以添加到任意支持修饰符的视图下面。例如:

  • TextButtonImage 等基础视图。
  • 容器视图:如 VStackHStackZStack
  • 复杂视图:如 ListScrollView 等。

示例 1:添加到基础视图 #

struct SheetExample: View {
    @State private var showSheet = false

    var body: some View {
        Button("Show Sheet") {
            showSheet = true
        }
        .sheet(isPresented: $showSheet) { // Sheet 可以直接加在 Button 视图上
            Text("This is a sheet")
                .font(.largeTitle)
                .padding()
        }
    }
}

示例 2:添加到容器视图 #

struct SheetExample: View {
    @State private var showSheet = false

    var body: some View {
        VStack {
            Text("Hello, SwiftUI!")
                .padding()
            Button("Show Sheet") {
                showSheet = true
            }
        }
        .sheet(isPresented: $showSheet) { // 添加到 VStack 容器上
            Text("This is a sheet")
                .font(.largeTitle)
                .padding()
        }
    }
}

示例 3:添加到 List 视图 #

struct SheetExample: View {
    @State private var showSheet = false

    var body: some View {
        List {
            Button("Show Sheet") {
                showSheet = true
            }
        }
        .sheet(isPresented: $showSheet) { // 添加到 List 上
            Text("Inside the sheet!")
        }
    }
}

.sheet 的触发条件 #

.sheet 的触发条件必须满足以下两种之一:

  1. 基于布尔值的弹窗触发(常见模式)。
  2. 基于绑定的视图模型或可选值的传递

1. 基于布尔值的 .sheet #

这是最常见的场景,通过 @State@Binding 的布尔值来控制 sheet 是否显示。

示例 #
struct SheetWithBoolExample: View {
    @State private var isSheetPresented = false

    var body: some View {
        Button("Present Sheet") {
            isSheetPresented = true
        }
        .sheet(isPresented: $isSheetPresented) { // 绑定布尔值,控制视图呈现
            VStack {
                Text("Hello, this is a sheet!")
                Button("Dismiss") {
                    isSheetPresented = false // 手动关闭 sheet
                }
            }
            .padding()
        }
    }
}

2. 基于模型或可选值的 .sheet(推荐用于复杂场景) #

sheet 可以绑定到一个 对象模型(optional 类型),当模型不为空时自动展示弹窗。

示例 #
struct SheetWithOptionalExample: View {
    @State private var selectedItem: String? = nil

    var body: some View {
        VStack {
            Button("Select Item 1") {
                selectedItem = "Item 1"
            }

            Button("Select Item 2") {
                selectedItem = "Item 2"
            }
        }
        .sheet(item: $selectedItem) { item in // 绑定到 Optional 类型
            Text("Selected item: \(item)")
                .font(.largeTitle)
                .padding()
        }
    }
}

解释:

  • selectedItem 不为 nil 时,sheet 自动弹出;
  • 传递的 item(一个字符串)用于展示不同的内容。

注意:多个 .sheet 注意不要冲突 #

如果多个 .sheet 同时作用于同一个视图层级(比如都加在同一个容器或顶层视图上),它们可能会导致冲突 或者只有一个生效

解决方案#

  • 确保只对触发 .sheet 的视图绑定一个状态值。
  • 如果多个弹窗需要共存,可以将 .sheet 放置到子视图中。
示例:多个 .sheet #
struct MultipleSheetExample: View {
    @State private var showFirstSheet = false
    @State private var showSecondSheet = false

    var body: some View {
        VStack {
            Button("Show First Sheet") {
                showFirstSheet = true
            }
            .sheet(isPresented: $showFirstSheet) {
                Text("First Sheet")
            }

            Button("Show Second Sheet") {
                showSecondSheet = true
            }
            .sheet(isPresented: $showSecondSheet) {
                Text("Second Sheet")
            }
        }
    }
}

专注点:

  • 每个 .sheet 使用独立的状态值(showFirstSheetshowSecondSheet)。

实践中不要做的操作(错误用法) #

  1. 多个 .sheet 修饰同一个视图。
Button("Tap Me") {
    // 尝试加载两个 `.sheet`,可能会发生冲突
}
.sheet(isPresented: $someCondition) {
    Text("Sheet 1")
}
.sheet(isPresented: $anotherCondition) {
    Text("Sheet 2")
}

问题: 这种情况下,SwiftUI 的运行时将无法正确决定哪个 .sheet 生效。

解决方案 #

  • 将不同的 .sheet 修饰符分配到不同的视图上;
  • 如果需要动态判断,可以用 @ViewBuilder 来动态加载。

总结 #

  1. .sheet 可以加在哪些视图下?

    • 任意支持修饰符的视图,包括 TextButton、布局容器(如 VStack)和列表(如 List/ScrollView)。
  2. 通过两种主要触发方式实现弹窗:

    • 布尔值绑定(简单、常见)。
    • 对象或可选值绑定(灵活、推荐用于复杂场景)。
  3. 注意事项:

    • 避免在同一视图上绑定多个 .sheet
    • 对于复杂需求,建议采用 ViewBuilder 或条件逻辑。

本文共 1238 字,上次修改于 Jan 10, 2025