在 SwiftUI 中,.sheet
是一个视图修饰符,用于展示一个模态弹窗(Modal Presentation
)。它的特点是与某个视图绑定,用于呈现一个子视图。以下是关于 .sheet
的使用规范和它可以添加在哪些视图下面的详细解答。
.sheet
可以加在哪些视图下面?
#
.sheet
可以添加到任意支持修饰符的视图下面。例如:
Text
、Button
、Image
等基础视图。- 容器视图:如
VStack
、HStack
、ZStack
。 - 复杂视图:如
List
、ScrollView
等。
示例 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. 基于布尔值的 .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
使用独立的状态值(showFirstSheet
和showSecondSheet
)。
实践中不要做的操作(错误用法) #
- 多个
.sheet
修饰同一个视图。
Button("Tap Me") {
// 尝试加载两个 `.sheet`,可能会发生冲突
}
.sheet(isPresented: $someCondition) {
Text("Sheet 1")
}
.sheet(isPresented: $anotherCondition) {
Text("Sheet 2")
}
问题: 这种情况下,SwiftUI 的运行时将无法正确决定哪个 .sheet
生效。
解决方案 #
- 将不同的
.sheet
修饰符分配到不同的视图上; - 如果需要动态判断,可以用
@ViewBuilder
来动态加载。
总结 #
.sheet
可以加在哪些视图下?- 任意支持修饰符的视图,包括
Text
、Button
、布局容器(如VStack
)和列表(如List
/ScrollView
)。
- 任意支持修饰符的视图,包括
通过两种主要触发方式实现弹窗:
- 布尔值绑定(简单、常见)。
- 对象或可选值绑定(灵活、推荐用于复杂场景)。
注意事项:
- 避免在同一视图上绑定多个
.sheet
。 - 对于复杂需求,建议采用
ViewBuilder
或条件逻辑。
- 避免在同一视图上绑定多个
PresentationDetent 的定义 #
在 SwiftUI 中,PresentationDetent
是 SwiftUI 5.0(iOS 15 和更高版本)引入的一个功能,用于控制 sheet
的弹出高度。通过 PresentationDetent
,开发者可以设置 sheet
的不同高度,并允许用户在这些高度之间切换,如 .medium
和 .large
。这一功能使得 sheet
的使用更加灵活和直观。
标准高度 #
PresentationDetent
提供了几个预定义的标准高度:
.medium
:高度覆盖屏幕的一半,大约 50%。.large
:高度覆盖整个屏幕(全屏)。.fraction(CGFloat)
(iOS 16 及更高版本):使用屏幕高度的一个特定百分比。
此外,在 UIKit 的 UISheetPresentationController
中,还可以自定义高度。
用法 #
要使用 PresentationDetent
,你需要在 sheet
修饰符中配置 presentationDetents
参数。
示例代码 #
import SwiftUI
struct ContentView: View {
@State private var isSheetPresented = false
var body: some View {
VStack {
Button("Present Sheet") {
isSheetPresented.toggle()
}
}
.sheet(isPresented: $isSheetPresented) {
SheetView()
.presentationDetents([.medium, .large]) // 定义预设的高度
}
}
}
struct SheetView: View {
var body: some View {
VStack {
Text("This is a sheet")
.font(.title)
.padding()
Spacer()
}
.background(Color.white)
}
}
自定义高度 #
在 iOS 16 及更高版本中,PresentationDetent
提供了一种更加灵活的方式来设置 sheet
的高度,即通过 .fraction(CGFloat)
,允许你定义基于屏幕高度的百分比。
示例代码:自定义高度 #
import SwiftUI
struct ContentView: View {
@State private var isSheetPresented = false
var body: some View {
VStack {
Button("Present Custom Height Sheet") {
isSheetPresented.toggle()
}
}
.sheet(isPresented: $isSheetPresented) {
SheetView()
.presentationDetents([.fraction(0.4)]) // 用屏幕高度的40%作为自定义高度
}
}
}
struct SheetView: View {
var body: some View {
VStack {
Text("This is a custom height sheet")
.font(.title)
.padding()
Spacer()
}
.background(Color.white)
}
}
多种高度支持和动态调整 #
你可以将多种高度结合使用,并允许用户通过拖动在这些高度之间进行切换。
示例代码:多种高度 #
import SwiftUI
struct ContentView: View {
@State private var isSheetPresented = false
var body: some View {
VStack {
Button("Present Resizable Sheet") {
isSheetPresented.toggle()
}
}
.sheet(isPresented: $isSheetPresented) {
SheetView()
.presentationDetents([.fraction(0.2), .medium, .large]) // 支持多种高度
.presentationDragIndicator(.visible) // 显示可拖动指示器
}
}
}
struct SheetView: View {
var body: some View {
VStack {
Text("This sheet supports multiple heights")
.font(.title)
.padding()
Spacer()
}
.background(Color.white)
}
}
解释: #
presentationDetents([.fraction(0.2), .medium, .large])
:提供用户20%屏幕高度、50%屏幕高度和全屏高度的选择。presentationDragIndicator(.visible)
:显示一个可拖动的指示器,让用户知道可以在不同高度之间拖动切换。
动态调整高度 #
你可以动态改变 sheet
的高度以响应用户交互或状态变化。
示例代码:动态调整高度 #
import SwiftUI
struct ContentView: View {
@State private var isSheetPresented = false
@State private var currentDetent: PresentationDetent = .medium
var body: some View {
VStack {
Button("Present Sheet") {
isSheetPresented.toggle()
}
Button("Toggle Height") {
currentDetent = (currentDetent == .medium) ? .large : .medium
}
}
.sheet(isPresented: $isSheetPresented) {
SheetView()
.presentationDetents([currentDetent])
}
}
}
struct SheetView: View {
var body: some View {
VStack {
Text("This sheet can change height dynamically")
.font(.title)
.padding()
Spacer()
}
.background(Color.white)
}
}
解释: #
- 动态变化:通过状态变量
currentDetent
控制sheet
的高度。 - 交互控制:用户点击按钮可以切换
sheet
的高度。
自定义高度的 UISheetPresentationController
#
如果需要更多自定义控制,可以通过 UIViewControllerRepresentable
结合 UISheetPresentationController
来实现。
示例代码:自定义 UISheetPresentationController
#
import SwiftUI
import UIKit
struct ContentView: View {
@State private var isSheetPresented = false
var body: some View {
VStack {
Button("Present Custom Sheet") {
isSheetPresented.toggle()
}
}
.sheet(isPresented: $isSheetPresented) {
CustomSheetViewController()
}
}
}
struct CustomSheetViewController: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> UIViewController {
let viewController = UIViewController()
viewController.view.backgroundColor = .white
let sheetController = viewController.sheetPresentationController
sheetController?.detents = [
.medium(),
.large(),
.custom { context in
return 150 // 自定义高度
}
]
sheetController?.prefersGrabberVisible = true // 显示抓手
return viewController
}
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {}
}
struct SheetView: View {
var body: some View {
VStack {
Text("This is a custom height sheet")
.font(.title)
.padding()
Spacer()
}
.background(Color.white)
}
}
解释: #
- 集成 UIKit:通过
UIViewControllerRepresentable
在 SwiftUI 中嵌入UIViewController
。 - 细致控制:使用
UISheetPresentationController
提供更细化的高度控制。
总结 #
PresentationDetent
使 SwiftUI 中的 sheet
控制更加灵活和强大。它允许开发者轻松设置 sheet
的高度,并且可以利用:
- 标准高度:
.medium
和.large
预定义高度。 - 自定义高度:通过
.fraction(CGFloat)
指定自定义百分比高度。 - 多高度支持:允许用户通过拖动在不同高度之间切换。
- 动态调整:根据用户交互或状态变化动态调整高度。
- 结合 UIKit:使用
UIViewControllerRepresentable
和UISheetPresentationController
实现更复杂的控制。
通过这些功能,你可以更加灵活地实现和控制 SwiftUI 中的 sheet
,满足不同的应用需求和用户体验。
在 SwiftUI 中,除了 PresentationDetent
,还有一些其他的 presentation
修饰符可以用来定制 sheet
的外观和行为。这些修饰符主要用于 sheet
,但有些也可以在其他类型的视图展示中使用。
让我们详细介绍一下这些 presentation
修饰符以及它们的用途。
其他 #
1. PresentationDetent #
PresentationDetent
控制 sheet
的高度,之前已经讲解过。常用的高度包括:
.medium
:覆盖一半屏幕。.large
:覆盖整个屏幕。.fraction(CGFloat)
:自定义高度,以屏幕高度的百分比表示。
2. presentationCornerRadius #
presentationCornerRadius
用于设置 sheet
的圆角半径。你可以通过此修饰符设置 sheet
顶部圆角的弧度,使它在视觉上符合应用的设计需求。
示例代码:设置圆角半径 #
import SwiftUI
struct ContentView: View {
@State private var isSheetPresented = false
var body: some View {
VStack {
Button("Present Sheet") {
isSheetPresented.toggle()
}
}
.sheet(isPresented: $isSheetPresented) {
SheetView()
.presentationDetents([.medium, .large])
.presentationCornerRadius(20) // 设置圆角半径
}
}
}
struct SheetView: View {
var body: some View {
VStack {
Text("This sheet has rounded corners")
.font(.title)
.padding()
Spacer()
}
.background(Color.white)
}
}
解释: #
presentationCornerRadius(20)
:将sheet
的顶部圆角半径设置为 20。
3. presentationDragIndicator #
presentationDragIndicator
用于设置 sheet
是否显示拖动指示器。拖动指示器是一个视觉提示,告诉用户可以对 sheet
进行拖动操作。
示例代码:显示拖动指示器 #
import SwiftUI
struct ContentView: View {
@State private var isSheetPresented = false
var body: some View {
VStack {
Button("Present Sheet") {
isSheetPresented.toggle()
}
}
.sheet(isPresented: $isSheetPresented) {
SheetView()
.presentationDetents([.medium, .large])
.presentationDragIndicator(.visible) // 显示拖动指示器
}
}
}
struct SheetView: View {
var body: some View {
VStack {
Text("This sheet has a drag indicator")
.font(.title)
.padding()
Spacer()
}
.background(Color.white)
}
}
解释: #
presentationDragIndicator(.visible)
:显示sheet
的拖动指示器。presentationDragIndicator(.hidden)
:隐藏sheet
的拖动指示器。
4. presentationBackgroundInteraction #
在 iOS 17 中,SwiftUI 引入了 presentationBackgroundInteraction
修饰符。该修饰符允许你控制 sheet
背后的交互行为。
示例代码:背景交互 #
import SwiftUI
struct ContentView: View {
@State private var isSheetPresented = false
var body: some View {
VStack {
Button("Present Sheet") {
isSheetPresented.toggle()
}
}
.sheet(isPresented: $isSheetPresented) {
SheetView()
.presentationDetents([.medium, .large])
.presentationBackgroundInteraction(.enabled) // 启用背景交互
}
}
}
struct SheetView: View {
var body: some View {
VStack {
Text("This sheet allows background interaction")
.font(.title)
.padding()
Spacer()
}
.background(Color.white)
}
}
解释: #
presentationBackgroundInteraction(.enabled)
:允许用户与sheet
背后的视图进行交互。presentationBackgroundInteraction(.disabled)
:禁用与sheet
背后的视图进行交互。
5. 结合使用多个修饰符 #
这些 presentation
修饰符通常可以组合使用,以实现更复杂和精细的控制。
示例代码:结合使用多个修饰符 #
import SwiftUI
struct ContentView: View {
@State private var isSheetPresented = false
var body: some View {
VStack {
Button("Present Sheet") {
isSheetPresented.toggle()
}
}
.sheet(isPresented: $isSheetPresented) {
SheetView()
.presentationDetents([.medium, .large])
.presentationCornerRadius(20)
.presentationDragIndicator(.visible)
.presentationBackgroundInteraction(.enabled) // 组合多个修饰符
}
}
}
struct SheetView: View {
var body: some View {
VStack {
Text("This sheet is fully customized")
.font(.title)
.padding()
Spacer()
}
.background(Color.white)
}
}
在非 sheet
中使用 presentation
修饰符
#
虽然这些 presentation
修饰符主要是为 sheet
设计的,但某些修饰符可能也可以用在 popover
等其他弹出式视图中,不过详细的支持情况需要查看具体的 SwiftUI 更新文档。当前这些修饰符在 sheet
中最为常用,并且提供了丰富的自定义选项。
总结 #
SwiftUI 提供了一组丰富的 presentation
修饰符,用于定制 sheet
的外观和行为。通过这些修饰符,你可以创建更符合设计需求和用户期望的 sheet
体验:
- PresentationDetent:控制
sheet
的高度。 - presentationCornerRadius:设置
sheet
顶部的圆角半径。 - presentationDragIndicator:显示或隐藏
sheet
的拖动指示器。 - presentationBackgroundInteraction:控制
sheet
背后的交互行为(iOS 17)。
这些修饰符提供了高度灵活性和可定制性,允许你精细控制 sheet
的呈现方式,以提升应用的用户体验。不妨在你的项目中尝试使用这些修饰符,看看它们能为你的应用带来怎样的提升。