Created
March 9, 2026 05:35
-
-
Save shnhrrsn/439d05ecf8344405bebf56f1a3108b31 to your computer and use it in GitHub Desktop.
This file contains hidden or 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 | |
| // MARK: - DismissInterceptor | |
| /// Overrides `@Environment(\.dismiss)` for child views by injecting a custom | |
| /// `Binding<PresentationMode>` via the deprecated `\.presentationMode` key path. | |
| /// | |
| /// `DismissAction` internally wraps `Binding<PresentationMode>` and calls its setter | |
| /// when `dismiss()` is invoked. By replacing that binding, we redirect dismiss to our | |
| /// handler instead of letting it close the window (macOS) or presentation (iOS). | |
| /// | |
| /// This lets views use `@Environment(\.dismiss)` normally without knowing they're | |
| /// inside a subscreen overlay. | |
| struct DismissInterceptor: ViewModifier { | |
| let onDismiss: @MainActor () -> Void | |
| /// presentationMode is get-only at the API level, but EnvironmentKey's subscript | |
| /// requires a WritableKeyPath — so the runtime cast succeeds. | |
| /// If Apple changes the internal layout, the cast returns nil and we fall through | |
| /// to a no-op (dismiss closes the window as before). | |
| private static let presentationModeKeyPath: WritableKeyPath<EnvironmentValues, Binding<PresentationMode>>? = { | |
| guard let keyPath = \EnvironmentValues.presentationMode as? WritableKeyPath else { | |
| assertionFailure() | |
| return nil | |
| } | |
| return keyPath | |
| }() | |
| func body(content: Content) -> some View { | |
| if let keyPath = Self.presentationModeKeyPath { | |
| let presented = true | |
| let pm = withUnsafeBytes(of: presented) { $0.load(as: PresentationMode.self) } | |
| let binding = Binding<PresentationMode>( | |
| get: { pm }, | |
| set: { _ in onDismiss() }, | |
| ) | |
| content.environment(keyPath, binding) | |
| } else { | |
| content | |
| } | |
| } | |
| } | |
| // MARK: - View + interceptDismiss | |
| extension View { | |
| /// Intercepts `@Environment(\.dismiss)` calls from child views, | |
| /// redirecting them to the provided handler. | |
| func interceptDismiss(_ handler: @MainActor @escaping () -> Void) -> some View { | |
| modifier(DismissInterceptor(onDismiss: handler)) | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment