Skip to Content
iOS🧭 NavigationSheet và FullScreen

Sheet và FullScreen trong SwiftUI

1. Sheet cơ bản

struct ContentView: View { @State private var showSheet = false var body: some View { Button("Show Sheet") { showSheet = true } .sheet(isPresented: $showSheet) { SheetContent() } } } struct SheetContent: View { @Environment(\.dismiss) var dismiss var body: some View { VStack { Text("Sheet Content") Button("Dismiss") { dismiss() } } } }

2. Full Screen Cover

struct ContentView: View { @State private var showFullScreen = false var body: some View { Button("Show Full Screen") { showFullScreen = true } .fullScreenCover(isPresented: $showFullScreen) { FullScreenView() } } }

3. Sheet với Item

struct ContentView: View { @State private var selectedProduct: Product? var body: some View { List(products) { product in Button(product.name) { selectedProduct = product } } .sheet(item: $selectedProduct) { product in ProductDetailSheet(product: product) } } }

4. Detents (Sheet Heights)

struct ContentView: View { @State private var showSheet = false var body: some View { Button("Show Sheet") { showSheet = true } .sheet(isPresented: $showSheet) { SheetContent() .presentationDetents([.medium, .large]) .presentationDragIndicator(.visible) } } } // Custom detent .presentationDetents([ .height(200), .fraction(0.5), .medium, .large ])

5. Sheet Customization

SheetContent() .presentationDetents([.medium, .large]) .presentationDragIndicator(.visible) .presentationCornerRadius(20) .presentationBackground(.ultraThinMaterial) .interactiveDismissDisabled() // Không thể swipe dismiss

6. Passing Data Back

Using Binding

struct ParentView: View { @State private var showEditor = false @State private var name = "" var body: some View { VStack { Text("Name: \(name)") Button("Edit") { showEditor = true } } .sheet(isPresented: $showEditor) { NameEditor(name: $name) } } } struct NameEditor: View { @Binding var name: String @Environment(\.dismiss) var dismiss var body: some View { VStack { TextField("Name", text: $name) Button("Save") { dismiss() } } .padding() } }

Using Callback

struct ParentView: View { @State private var showPicker = false @State private var selectedColor = Color.blue var body: some View { Circle() .fill(selectedColor) .onTapGesture { showPicker = true } .sheet(isPresented: $showPicker) { ColorPickerSheet { color in selectedColor = color } } } } struct ColorPickerSheet: View { @Environment(\.dismiss) var dismiss var onSelect: (Color) -> Void let colors: [Color] = [.red, .blue, .green, .orange] var body: some View { VStack { ForEach(colors, id: \.self) { color in Button { onSelect(color) dismiss() } label: { Circle().fill(color).frame(width: 50, height: 50) } } } } }

7. Confirmation antes de dismiss

struct UnsavedChangesSheet: View { @Environment(\.dismiss) var dismiss @State private var hasChanges = false @State private var showConfirmation = false var body: some View { VStack { TextField("Content", text: .constant("")) .onChange(of: text) { hasChanges = true } Button("Save") { dismiss() } } .interactiveDismissDisabled(hasChanges) .onDisappear { if hasChanges { showConfirmation = true } } .confirmationDialog("Discard changes?", isPresented: $showConfirmation) { Button("Discard", role: .destructive) { dismiss() } Button("Cancel", role: .cancel) { } } } }

8. Multiple Sheets

struct ContentView: View { @State private var activeSheet: SheetType? enum SheetType: Identifiable { case settings, profile, help var id: Self { self } } var body: some View { VStack { Button("Settings") { activeSheet = .settings } Button("Profile") { activeSheet = .profile } Button("Help") { activeSheet = .help } } .sheet(item: $activeSheet) { sheet in switch sheet { case .settings: SettingsView() case .profile: ProfileView() case .help: HelpView() } } } }

📝 Tóm tắt

ModifierMục đích
.sheetModal sheet
.fullScreenCoverFull screen modal
.presentationDetentsSheet height
@Environment(\.dismiss)Dismiss action
Last updated on