Last active
August 16, 2024 04:11
-
-
Save StewartLynch/5db32fc2b831f25fdd22f5951ac8d274 to your computer and use it in GitHub Desktop.
Global Sheets. Orginal idea via Mohammad Azam https://www.youtube.com/watch?v=mj2cRsvYH44 but modified to use a sheet conforming to View, a View Modifier and the new @entry macro
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SwiftUI | |
enum Sheet: Identifiable, View { // Remove Hashabke but add View conformance | |
case settings | |
case contact(String) | |
case nameEntry(Binding<String>) | |
var id: String {String(describing: self)} // Now Sheet does not have to conform to Hashable | |
var body: some View { // Now that you have View, you have a body | |
switch self { | |
case .settings: | |
// This can be any view | |
Text("Settings") | |
case .contact(let name): | |
// This can be any view so long as the view is expecting a String property by injection | |
Text("Contacting \(name)") | |
case .nameEntry(let name): // This is a binding | |
// Enum with a Binding | |
VStack { | |
Text("Enter your name") | |
TextField("Name", text: name) | |
} | |
} | |
} | |
} | |
struct SheetAction { | |
typealias Action = (Sheet, (() -> Void)?) -> Void | |
let action: Action | |
func callAsFunction(_ sheet: Sheet, _ dismiss: (() -> Void)?) { | |
action(sheet, dismiss) | |
} | |
func callAsFunction(_ sheet: Sheet) { | |
action(sheet, nil) | |
} | |
} | |
// Use @Entry Macro to display showSHeet | |
extension EnvironmentValues { | |
@Entry var showSheet: SheetAction = SheetAction { _, _ in } | |
} | |
// This is a custom modifier to enable the new showSheet methods | |
struct PresentSheetModifier: ViewModifier { | |
@Binding var selectedSheet: Sheet? | |
var onSheetDismiss: Binding<(() -> Void)?>? | |
func body(content: Content) -> some View { | |
content | |
.environment(\.showSheet, SheetAction(action: { sheet, dismiss in | |
selectedSheet = sheet | |
onSheetDismiss?.wrappedValue = dismiss | |
})) | |
.sheet(item: $selectedSheet, onDismiss: onSheetDismiss?.wrappedValue) { $0 } | |
} | |
} | |
extension View { | |
// Initializer with `onSheetDismiss` | |
func presentSheet(selectedSheet: Binding<Sheet?>, onSheetDismiss: Binding<(() -> Void)?>) -> some View { | |
modifier(PresentSheetModifier(selectedSheet: selectedSheet, onSheetDismiss: onSheetDismiss)) | |
} | |
// Initializer without `onSheetDismiss` | |
func presentSheet(selectedSheet: Binding<Sheet?>) -> some View { | |
modifier(PresentSheetModifier(selectedSheet: selectedSheet, onSheetDismiss: nil)) | |
} | |
} | |
struct ContentView: View { | |
@Environment(\.showSheet) private var showSheet | |
@State private var name: String = "No Name" | |
var body: some View { | |
VStack { | |
Text(name) | |
.font(.title) | |
Button("Show Settings Screen") { | |
// With Dismiss action | |
showSheet(.settings) { | |
print("Setting sheet Dismissed") | |
} | |
} | |
Button("Show Contact Screen") { | |
showSheet(.contact("John Doe")) | |
} | |
Button("Get name") { | |
showSheet(.nameEntry($name)) | |
} | |
} | |
.padding() | |
} | |
} | |
#Preview { | |
// For preview only as this is what you will have in the app launch file @Main | |
@Previewable @State var selectedSheet: Sheet? | |
@Previewable @State var onSheetDismiss: (() -> Void)? | |
ContentView() | |
.presentSheet(selectedSheet: $selectedSheet, onSheetDismiss: $onSheetDismiss) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment