From 7e2ebac722c1a07665972a5f7ad1c5d814dee470 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Helge=20He=C3=9F?= Date: Sat, 30 Apr 2022 14:44:49 +0200 Subject: [PATCH] Drop the explicit sheet/nav presentations As discussed in Issue #3. Users of the framework will mostly use automatic presentations. And if they want to do something custom, they still can. Just use the bindings available for the active VC and attach those to the `isActive` binding of the View presentation. --- .../Presentations/PushPresentation.swift | 165 ----------------- .../Presentations/SheetPresentation.swift | 168 ------------------ ViewController.xcodeproj/project.pbxproj | 12 -- 3 files changed, 345 deletions(-) delete mode 100644 Sources/ViewController/Presentations/PushPresentation.swift delete mode 100644 Sources/ViewController/Presentations/SheetPresentation.swift diff --git a/Sources/ViewController/Presentations/PushPresentation.swift b/Sources/ViewController/Presentations/PushPresentation.swift deleted file mode 100644 index a24fc36..0000000 --- a/Sources/ViewController/Presentations/PushPresentation.swift +++ /dev/null @@ -1,165 +0,0 @@ -// -// PushPresentation.swift -// ViewController -// -// Created by Helge Heß. -// Copyright © 2022 ZeeZide GmbH. All rights reserved. -// - -import SwiftUI - -public extension View { - - /** - * Controls how a specific ViewController is being presented in `.custom` - * mode. - * - * It does NOT do the actual presentation! I.e. `.present(MyViewController)` - * still has to be called. - * - * This peeks into the ``ViewController/presentedVC`` of the current - * ``ViewController`` (which is stored in the environment!) - * - * How to use: - * ``` - * ContentView() - * .presentInSheet(WidgetViewVC.self) { - * NavigationView { - * ChildView() - * } - * .navigationViewStyle(.stack) - * } - * .presentInNavigation(AddWidgetVC.self) { - * AddWidgetVC.ContentView() - * } - * .presentInNavigation(ViewConfigVC.self) { - * AddWidgetVC.ContentView() - * } - * ``` - * - * Note: This is using a `background` for the `NavigationLink`. - * Use a ``PushLink`` for a real ``NavigationLink``. - */ - func presentInNavigation(_ vc: VC.Type, - @ViewBuilder content: @escaping () -> V) - -> some View - where VC: ViewController, V: View - { - // Note: The explicit specialization is NECESSARY, otherwise the wrong ones - // are picked up! - self.modifier(PushPresentation(destination: content)) - } - - /** - * Controls how a specific ``ViewController`` is being presented in `.custom` - * mode. - * - * It does NOT do the actual presentation! I.e. `.present(MyViewController)` - * still has to be called. - * - * This peeks into the ``ViewController/presentedVC`` of the current - * ``ViewController`` (which is stored in the environment!) - * - * How to use: - * ``` - * ContentView() - * .presentInSheet(WidgetViewVC.self) - * .presentInNavigation(AddWidgetVC.self) - * .presentInNavigation(ViewConfigVC.self) - * ``` - */ - @inlinable - func presentInNavigation(_ vc: VC.Type) -> some View { - presentInNavigation(vc, content: { RenderContentView() }) - } - - /** - * Helper to avoid using ``presentInNavigation`` with a ``ViewController`` - * that doesn't have a proper ``ContentView``. - */ - @available(*, unavailable, - message: "The ViewController needs a proper `ContentView`") - func presentInNavigation(_ vc: VC.Type) -> some View - where VC.ContentView == DefaultViewControllerView - { - presentInNavigation(vc, content: { RenderContentView() }) - } -} - -/** - * Controls how a specific ViewController is being presented in `.custom` mode. - * It does NOT do the actual presentation! - * - * This peeks into the ``ViewController/presentedVC`` of the current - * ``ViewController`` (which is stored in the environment!) - * - * How to use: - * ``` - * ContentView() - * .presentInSheet(WidgetViewVC.self) { - * NavigationView { - * ChildView() - * } - * .navigationViewStyle(.stack) - * } - * .presentInNavigation(AddWidgetVC.self) { - * AddWidgetVC.ContentView() - * } - * .presentInNavigation(ViewConfigVC.self) { - * AddWidgetVC.ContentView() - * } - * ``` - * - * Note: This is using a `background` for the `NavigationLink`. - * Use a ``PushLink`` for a real ``NavigationLink``. - */ -@usableFromInline -struct PushPresentation: ViewModifier - where DestinationVC: ViewController, V: View -{ - - @EnvironmentObject var parent : AnyViewController - - private let destination : () -> V - private let mode : ViewControllerPresentationMode - - public init(destination: @escaping () -> V) { - self.destination = destination - self.mode = .custom - } - @usableFromInline - internal init(mode: ViewControllerPresentationMode, - destination: @escaping () -> V) - { - self.destination = destination - self.mode = mode - } - - @usableFromInline - func body(content: Content) -> some View { - content - .background( - NavigationLink( - // TODO: This needs an optional extra condition - isActive: parent.isPresenting(DestinationVC.self, mode: mode), - destination: { - if let presentedVC = - parent.presentedViewController(of: DestinationVC.self, - mode: mode) - { - destination() - .environment(\.viewControllerPresentationMode, .navigation) - .controlled(by: presentedVC) - .navigationTitle(presentedVC.navigationTitle) - } - else { - TypeMismatchInfoView( - parent: parent, expectedMode: mode - ) - } - }, - label: { Color.clear } // TBD: EmptyView? - ) - ) - } -} diff --git a/Sources/ViewController/Presentations/SheetPresentation.swift b/Sources/ViewController/Presentations/SheetPresentation.swift deleted file mode 100644 index a7a8367..0000000 --- a/Sources/ViewController/Presentations/SheetPresentation.swift +++ /dev/null @@ -1,168 +0,0 @@ -// -// SheetPresentation.swift -// ViewController -// -// Created by Helge Heß. -// Copyright © 2022 ZeeZide GmbH. All rights reserved. -// - -import SwiftUI - -public extension View { - - /** - * Controls how a specific ViewController is being presented in `.custom` - * mode. - * - * It does NOT do the actual presentation! I.e. `.present(MyViewController)` - * still has to be called. - * - * This peeks into the ``ViewController/presentedVC`` of the current - * ``ViewController`` (which is stored in the environment!) - * - * How to use: - * ``` - * ContentView() - * .presentInSheet(WidgetViewVC.self) { - * NavigationView { - * ChildView() - * } - * .navigationViewStyle(.stack) - * } - * .presentInSheet(AddWidgetVC.self) { - * AddWidgetVC.ContentView() - * } - * .presentInSheet(ViewConfigVC.self) { - * AddWidgetVC.ContentView() - * } - * ``` - */ - func presentInSheet(_ vc: VC.Type, - @ViewBuilder content: @escaping () -> V) - -> some View - where VC: ViewController, V: View - { - // Note: The explicit specialization is NECESSARY, otherwise the wrong ones - // are picked up! - /* - * This has issues in the content closure: - * .presentInSheet(ConfigVC.self) { - * // Passing parameters (like `widget`) here, ends up w/ the "first" - * // widget of the type. - * ConfigVC.ContentView() - * } - * Environment objects do seem to be fine though? So something strange - * happening w/ the capture here. - */ - self.modifier(SheetPresentation(destination: content)) - } - - /** - * Controls how a specific ``ViewController`` is being presented in `.custom` - * mode. - * - * It does NOT do the actual presentation! I.e. `.present(MyViewController)` - * still has to be called. - * - * This peeks into the ``ViewController/presentedVC`` of the current - * ``ViewController`` (which is stored in the environment!) - * - * How to use: - * ``` - * ContentView() - * .presentInSheet(WidgetViewVC.self) - * .presentInSheet(AddWidgetVC.self) - * .presentInSheet(ViewConfigVC.self) - * ``` - */ - @inlinable - func presentInSheet(_ vc: VC.Type) -> some View { - presentInSheet(vc, content: { RenderContentView() }) - } - - /** - * Helper to avoid using ``presentInSheet`` with a ``ViewController`` - * that doesn't have a proper ``ContentView``. - */ - @available(*, unavailable, - message: "The ViewController needs a proper `ContentView`") - func presentInSheet(_ vc: VC.Type) -> some View - where VC.ContentView == DefaultViewControllerView - { - presentInSheet(vc, content: { RenderContentView() }) - } -} - -/** - * Controls how a specific ViewController is being presented in `.custom` mode. - * It does NOT do the actual presentation! - * - * This peeks into the ``ViewController/presentedVC`` of the current - * ``ViewController`` (which is stored in the environment!) - * - * How to use: - * ``` - * ContentView() - * .presentInSheet(WidgetViewVC.self) { - * NavigationView { - * ChildView() - * } - * .navigationViewStyle(.stack) - * } - * .presentInSheet(AddWidgetVC.self) { - * AddWidgetVC.ContentView() - * } - * .presentInSheet(ViewConfigVC.self) { - * AddWidgetVC.ContentView() - * } - * ``` - */ -@usableFromInline -struct SheetPresentation: ViewModifier - where DestinationVC: ViewController, V: View -{ - - @EnvironmentObject private var parent : AnyViewController - - private let destination : () -> V - private let mode : ViewControllerPresentationMode - - public init(destination: @escaping () -> V) { - self.destination = destination - self.mode = .custom - } - @usableFromInline - internal init(mode: ViewControllerPresentationMode, - destination: @escaping () -> V) - { - self.destination = destination - self.mode = mode - } - - private func isActive(_ vc: _ViewController) -> Bool { - // TODO: This needs an optional extra condition - vc is DestinationVC - } - - @usableFromInline - func body(content: Content) -> some View { - content - .sheet(isPresented: parent.isPresenting(mode: mode, isActive)) { - if let presentedVC : DestinationVC = parent - .presentedViewController(of: DestinationVC.self, mode: mode) - { - // TODO: This is tricky. The destination here can capture - // the incorrect VC, because the sheet presentation condition - // only checks the type! - destination() - .environment(\.viewControllerPresentationMode, .sheet) - .controlled(by: presentedVC) - } - else { - TypeMismatchInfoView( - parent: parent, expectedMode: mode - ) - } - } - } -} diff --git a/ViewController.xcodeproj/project.pbxproj b/ViewController.xcodeproj/project.pbxproj index c62d3f1..e1ccee0 100644 --- a/ViewController.xcodeproj/project.pbxproj +++ b/ViewController.xcodeproj/project.pbxproj @@ -19,10 +19,6 @@ E80050272816EE3400E4805C /* ViewControllerInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = E80050252816EE3400E4805C /* ViewControllerInfo.swift */; }; E8283B432819629E00467F10 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8283B422819629E00467F10 /* ContentView.swift */; }; E8283B442819629E00467F10 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8283B422819629E00467F10 /* ContentView.swift */; }; - E836E5A0280EEA870001B85E /* PushPresentation.swift in Sources */ = {isa = PBXBuildFile; fileRef = E836E59F280EEA870001B85E /* PushPresentation.swift */; }; - E836E5A1280EEA870001B85E /* PushPresentation.swift in Sources */ = {isa = PBXBuildFile; fileRef = E836E59F280EEA870001B85E /* PushPresentation.swift */; }; - E836E5A3280EEACA0001B85E /* SheetPresentation.swift in Sources */ = {isa = PBXBuildFile; fileRef = E836E5A2280EEACA0001B85E /* SheetPresentation.swift */; }; - E836E5A4280EEACA0001B85E /* SheetPresentation.swift in Sources */ = {isa = PBXBuildFile; fileRef = E836E5A2280EEACA0001B85E /* SheetPresentation.swift */; }; E836E5AD280EECD50001B85E /* PushLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = E836E5AC280EECD50001B85E /* PushLink.swift */; }; E836E5AE280EECD50001B85E /* PushLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = E836E5AC280EECD50001B85E /* PushLink.swift */; }; E83ADA662814267600D98D82 /* Containment.swift in Sources */ = {isa = PBXBuildFile; fileRef = E83ADA652814267600D98D82 /* Containment.swift */; }; @@ -77,8 +73,6 @@ E80050252816EE3400E4805C /* ViewControllerInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewControllerInfo.swift; sourceTree = ""; }; E8283B422819629E00467F10 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; E836E59E280EEA0A0001B85E /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; - E836E59F280EEA870001B85E /* PushPresentation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushPresentation.swift; sourceTree = ""; }; - E836E5A2280EEACA0001B85E /* SheetPresentation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SheetPresentation.swift; sourceTree = ""; }; E836E5AC280EECD50001B85E /* PushLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushLink.swift; sourceTree = ""; }; E83ADA652814267600D98D82 /* Containment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Containment.swift; sourceTree = ""; }; E83ADA77281444B000D98D82 /* TypeMismatchInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TypeMismatchInfoView.swift; sourceTree = ""; }; @@ -150,8 +144,6 @@ E8BA6EBE2815914F00FA6C5C /* ViewControllerPresentation.swift */, E8E7066827F2014F00F50160 /* PresentationMode.swift */, E800501F2816C40600E4805C /* AutoPresentation.swift */, - E836E59F280EEA870001B85E /* PushPresentation.swift */, - E836E5A2280EEACA0001B85E /* SheetPresentation.swift */, ); path = Presentations; sourceTree = ""; @@ -354,7 +346,6 @@ E88DCE802812D6A700CD5203 /* Subscriptions.swift in Sources */, E8BA6EBF2815914F00FA6C5C /* ViewControllerPresentation.swift in Sources */, E88DCE702812C83C00CD5203 /* ReExports.swift in Sources */, - E836E5A3280EEACA0001B85E /* SheetPresentation.swift in Sources */, E83ADA662814267600D98D82 /* Containment.swift in Sources */, E800501D2816B09A00E4805C /* DebugOverlay.swift in Sources */, E83ADA78281444B000D98D82 /* TypeMismatchInfoView.swift in Sources */, @@ -366,7 +357,6 @@ E88DCE862812DF8200CD5203 /* RepresentedObject.swift in Sources */, E88DCE832812DD8B00CD5203 /* Title.swift in Sources */, E80050202816C40600E4805C /* AutoPresentation.swift in Sources */, - E836E5A0280EEA870001B85E /* PushPresentation.swift in Sources */, E800501A2816B00E00E4805C /* DebugMode.swift in Sources */, E88DCE892812E9CD00CD5203 /* Presentation.swift in Sources */, E88DCE7D2812D3A300CD5203 /* ViewControllerStorage.swift in Sources */, @@ -391,7 +381,6 @@ E88DCE812812D6A700CD5203 /* Subscriptions.swift in Sources */, E8BA6EC02815914F00FA6C5C /* ViewControllerPresentation.swift in Sources */, E88DCE712812C83C00CD5203 /* ReExports.swift in Sources */, - E836E5A4280EEACA0001B85E /* SheetPresentation.swift in Sources */, E83ADA672814267600D98D82 /* Containment.swift in Sources */, E800501E2816B09A00E4805C /* DebugOverlay.swift in Sources */, E83ADA79281444B000D98D82 /* TypeMismatchInfoView.swift in Sources */, @@ -403,7 +392,6 @@ E88DCE872812DF8200CD5203 /* RepresentedObject.swift in Sources */, E88DCE842812DD8B00CD5203 /* Title.swift in Sources */, E80050212816C40600E4805C /* AutoPresentation.swift in Sources */, - E836E5A1280EEA870001B85E /* PushPresentation.swift in Sources */, E800501B2816B00E00E4805C /* DebugMode.swift in Sources */, E88DCE8A2812E9CD00CD5203 /* Presentation.swift in Sources */, E88DCE7E2812D3A300CD5203 /* ViewControllerStorage.swift in Sources */,