Last active
December 7, 2019 04:52
-
-
Save petercv/3fba967a69b262901053fc8638b7851b to your computer and use it in GitHub Desktop.
Fixed plus improved PresentationLink
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
// Fixes PresentationLink so that a modal can be opened multiple times. | |
// | |
// Also adds .isModalInPresentation(_ value: Bool) modifier to control | |
// if the modal can be closed or not. | |
// | |
// Usage: | |
// | |
// In your SceneDelegate.swift: | |
// | |
// let presentationHelper = PresentationHelper() | |
// let viewController = UIHostingController(rootView: ContentView().environmentObject(presentationHelper)) | |
// presentationHelper.viewController = viewController | |
// window.rootViewController = viewController | |
// | |
// After this you can use PresentationLink as normal (as long as this code is part of your module). | |
// | |
// You can close the modal using the default swipe gesture or by using the @Environment(\.isPresented) | |
// binding. For example: | |
// | |
// @Environment(\.isPresented) var isPresented | |
// ... | |
// Button(action: { isPresented?.value = false }) { | |
// Text("Done") | |
// } | |
// | |
// You can also use the PresentationHelper to open modals directly from an action: | |
// | |
// @EnvironmentObject presentationHelper: PresentationHelper | |
// ... | |
// Button(action: { self.presentationHelper.present(Text("Hello!")) }) { | |
// Text("Say Hello!") | |
// } | |
import Foundation | |
import SwiftUI | |
import Combine | |
struct ModalContent<Content: View>: View { | |
@EnvironmentObject var presentationHelper: PresentationHelper | |
public let content: Content | |
var body: some View { | |
content | |
.environment(\.isPresented, $presentationHelper.isPresented) | |
} | |
} | |
class PresentationHelper: BindableObject { | |
public let didChange = PassthroughSubject<Void, Never>() | |
public var isPresented: Bool = false { | |
didSet { | |
if !isPresented && isModalInPresentation { | |
isPresented = true | |
} else if !isPresented { | |
viewController?.dismiss(animated: true) | |
} | |
} | |
} | |
public var isModalInPresentation: Bool = false { | |
didSet { | |
viewController?.isModalInPresentation = isModalInPresentation | |
} | |
} | |
public var viewController: UIViewController? { | |
didSet { | |
didChange.send() | |
} | |
} | |
public func present<Destination: View>(_ destination: Destination, onDismiss: (() -> Void)? = nil) { | |
let presentationHelper = PresentationHelper() | |
let rootView = ModalContent(content: destination).environmentObject(presentationHelper) | |
let viewController = UIHostingController(rootView: rootView) | |
presentationHelper.viewController = viewController | |
self.viewController?.present(viewController, animated: true, completion: onDismiss) | |
} | |
} | |
public struct PresentationLink<Label: View, Destination: View>: View { | |
@EnvironmentObject private var presentationHelper: PresentationHelper | |
public let destination: Destination | |
public let label: () -> Label | |
public init(_ destination: Destination, label: @escaping () -> Label) { | |
self.destination = destination | |
self.label = label | |
} | |
public var body: some View { | |
Button(action: present) { | |
label() | |
} | |
} | |
private func present() { | |
presentationHelper.present(destination) | |
} | |
} | |
public struct IsModalInPresentationModifier: ViewModifier { | |
@EnvironmentObject var presentationHelper: PresentationHelper | |
private let value: Bool | |
public init(_ value: Bool) { | |
self.value = value | |
} | |
public func body(content: Content) -> some View { | |
// dirty way of modifying the isModalInPresentation property, but currently don't know of any other way | |
presentationHelper.isModalInPresentation = self.value | |
return content.offset(x: 0, y: 0) // need to modify the view else our modifier will not be called | |
} | |
} | |
public extension View { | |
func isModalInPresentation(_ value: Bool) -> Modified<IsModalInPresentationModifier> { | |
return modifier(IsModalInPresentationModifier(value)) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I don't know if I'm doing this wrong but I'm supposed to just add this as a file to my project and then add the stuff into SceneDelegate right? Whenever I do this I just get a lot of errors in both PresentationLink.swift and in my SceneDelegate. Any help with this would be much appreciated.