Skip to content

Instantly share code, notes, and snippets.

@StewartLynch
Last active August 16, 2024 04:11
Show Gist options
  • Save StewartLynch/5db32fc2b831f25fdd22f5951ac8d274 to your computer and use it in GitHub Desktop.
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
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