Remove MatchedGeometryPresentation
This commit is contained in:
parent
988b9b4793
commit
a28798bf52
@ -19,7 +19,6 @@ let package = Package(
|
|||||||
.package(path: "../Pachyderm"),
|
.package(path: "../Pachyderm"),
|
||||||
.package(path: "../InstanceFeatures"),
|
.package(path: "../InstanceFeatures"),
|
||||||
.package(path: "../TuskerComponents"),
|
.package(path: "../TuskerComponents"),
|
||||||
.package(path: "../MatchedGeometryPresentation"),
|
|
||||||
.package(path: "../TuskerPreferences"),
|
.package(path: "../TuskerPreferences"),
|
||||||
.package(path: "../UserAccounts"),
|
.package(path: "../UserAccounts"),
|
||||||
.package(path: "../GalleryVC"),
|
.package(path: "../GalleryVC"),
|
||||||
@ -33,7 +32,6 @@ let package = Package(
|
|||||||
"Pachyderm",
|
"Pachyderm",
|
||||||
"InstanceFeatures",
|
"InstanceFeatures",
|
||||||
"TuskerComponents",
|
"TuskerComponents",
|
||||||
"MatchedGeometryPresentation",
|
|
||||||
"TuskerPreferences",
|
"TuskerPreferences",
|
||||||
"UserAccounts",
|
"UserAccounts",
|
||||||
"GalleryVC",
|
"GalleryVC",
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
.DS_Store
|
|
||||||
/.build
|
|
||||||
/Packages
|
|
||||||
/*.xcodeproj
|
|
||||||
xcuserdata/
|
|
||||||
DerivedData/
|
|
||||||
.swiftpm/config/registries.json
|
|
||||||
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
|
|
||||||
.netrc
|
|
@ -1,29 +0,0 @@
|
|||||||
// swift-tools-version: 6.0
|
|
||||||
// The swift-tools-version declares the minimum version of Swift required to build this package.
|
|
||||||
|
|
||||||
import PackageDescription
|
|
||||||
|
|
||||||
let package = Package(
|
|
||||||
name: "MatchedGeometryPresentation",
|
|
||||||
platforms: [
|
|
||||||
.iOS(.v15),
|
|
||||||
],
|
|
||||||
products: [
|
|
||||||
// Products define the executables and libraries a package produces, making them visible to other packages.
|
|
||||||
.library(
|
|
||||||
name: "MatchedGeometryPresentation",
|
|
||||||
targets: ["MatchedGeometryPresentation"]),
|
|
||||||
],
|
|
||||||
targets: [
|
|
||||||
// Targets are the basic building blocks of a package, defining a module or a test suite.
|
|
||||||
// Targets can depend on other targets in this package and products from dependencies.
|
|
||||||
.target(
|
|
||||||
name: "MatchedGeometryPresentation",
|
|
||||||
swiftSettings: [
|
|
||||||
.swiftLanguageMode(.v5)
|
|
||||||
]),
|
|
||||||
// .testTarget(
|
|
||||||
// name: "MatchedGeometryPresentationTests",
|
|
||||||
// dependencies: ["MatchedGeometryPresentation"]),
|
|
||||||
]
|
|
||||||
)
|
|
@ -1,125 +0,0 @@
|
|||||||
//
|
|
||||||
// MatchedGeometryModifiers.swift
|
|
||||||
// MatchGeom
|
|
||||||
//
|
|
||||||
// Created by Shadowfacts on 4/24/23.
|
|
||||||
//
|
|
||||||
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
extension View {
|
|
||||||
public func matchedGeometryPresentation<ID: Hashable, Presented: View>(id: Binding<ID?>, backgroundColor: UIColor, @ViewBuilder presenting: () -> Presented) -> some View {
|
|
||||||
self.modifier(MatchedGeometryPresentationModifier(id: id, backgroundColor: backgroundColor, presented: presenting()))
|
|
||||||
}
|
|
||||||
|
|
||||||
public func matchedGeometrySource<ID: Hashable, ID2: Hashable>(id: ID, presentationID: ID2) -> some View {
|
|
||||||
self.modifier(MatchedGeometrySourceModifier(id: AnyHashable(id), presentationID: AnyHashable(presentationID), matched: { AnyView(self) }))
|
|
||||||
}
|
|
||||||
|
|
||||||
public func matchedGeometryDestination<ID: Hashable>(id: ID) -> some View {
|
|
||||||
self.modifier(MatchedGeometryDestinationModifier(id: AnyHashable(id), matched: self))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private struct MatchedGeometryPresentationModifier<ID: Hashable, Presented: View>: ViewModifier {
|
|
||||||
@Binding var id: ID?
|
|
||||||
let backgroundColor: UIColor
|
|
||||||
let presented: Presented
|
|
||||||
@StateObject private var state = MatchedGeometryState()
|
|
||||||
|
|
||||||
private var isPresented: Binding<Bool> {
|
|
||||||
Binding {
|
|
||||||
id != nil
|
|
||||||
} set: {
|
|
||||||
if $0 {
|
|
||||||
fatalError()
|
|
||||||
} else {
|
|
||||||
id = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func body(content: Content) -> some View {
|
|
||||||
content
|
|
||||||
.environmentObject(state)
|
|
||||||
.backgroundPreferenceValue(MatchedGeometrySourcesKey.self, { sources in
|
|
||||||
Color.clear
|
|
||||||
.presentViewController(makeVC(allSources: sources), isPresented: isPresented)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
private func makeVC(allSources: [SourceKey: (AnyView, CGRect)]) -> () -> UIViewController {
|
|
||||||
return {
|
|
||||||
// force unwrap is safe, this closure is only called when being presented so we must have an id
|
|
||||||
let id = AnyHashable(id!)
|
|
||||||
return MatchedGeometryViewController(
|
|
||||||
presentationID: id,
|
|
||||||
content: presented,
|
|
||||||
state: state,
|
|
||||||
backgroundColor: backgroundColor
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private struct MatchedGeometrySourceModifier: ViewModifier {
|
|
||||||
let id: AnyHashable
|
|
||||||
let presentationID: AnyHashable
|
|
||||||
let matched: () -> AnyView
|
|
||||||
@EnvironmentObject private var state: MatchedGeometryState
|
|
||||||
|
|
||||||
func body(content: Content) -> some View {
|
|
||||||
content
|
|
||||||
.background(GeometryReader { proxy in
|
|
||||||
Color.clear
|
|
||||||
.preference(key: MatchedGeometryDestinationFrameKey.self, value: proxy.frame(in: .global))
|
|
||||||
.onPreferenceChange(MatchedGeometryDestinationFrameKey.self) { newValue in
|
|
||||||
if let newValue {
|
|
||||||
state.sources[SourceKey(presentationID: presentationID, matchedID: id)] = (matched, newValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.opacity(state.animating && state.presentationID == presentationID ? 0 : 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private struct MatchedGeometryDestinationModifier<Matched: View>: ViewModifier {
|
|
||||||
let id: AnyHashable
|
|
||||||
let matched: Matched
|
|
||||||
@EnvironmentObject private var state: MatchedGeometryState
|
|
||||||
|
|
||||||
func body(content: Content) -> some View {
|
|
||||||
content
|
|
||||||
.background(GeometryReader { proxy in
|
|
||||||
Color.clear
|
|
||||||
.preference(key: MatchedGeometryDestinationFrameKey.self, value: proxy.frame(in: .global))
|
|
||||||
.onPreferenceChange(MatchedGeometryDestinationFrameKey.self) { newValue in
|
|
||||||
if let newValue,
|
|
||||||
// ignore intermediate layouts that may happen while the dismiss animation is happening
|
|
||||||
state.mode != .dismissing {
|
|
||||||
state.destinations[id] = (AnyView(matched), newValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.opacity(state.animating ? 0 : 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private struct MatchedGeometryDestinationFrameKey: PreferenceKey {
|
|
||||||
static let defaultValue: CGRect? = nil
|
|
||||||
static func reduce(value: inout CGRect?, nextValue: () -> CGRect?) {
|
|
||||||
value = nextValue()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private struct MatchedGeometrySourcesKey: PreferenceKey {
|
|
||||||
static let defaultValue: [SourceKey: (AnyView, CGRect)] = [:]
|
|
||||||
static func reduce(value: inout Value, nextValue: () -> Value) {
|
|
||||||
value.merge(nextValue(), uniquingKeysWith: { _, new in new })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct SourceKey: Hashable {
|
|
||||||
let presentationID: AnyHashable
|
|
||||||
let matchedID: AnyHashable
|
|
||||||
}
|
|
@ -1,239 +0,0 @@
|
|||||||
//
|
|
||||||
// MatchedGeometryViewController.swift
|
|
||||||
// MatchGeom
|
|
||||||
//
|
|
||||||
// Created by Shadowfacts on 4/24/23.
|
|
||||||
//
|
|
||||||
|
|
||||||
import SwiftUI
|
|
||||||
import Combine
|
|
||||||
|
|
||||||
private let mass: CGFloat = 1
|
|
||||||
private let presentStiffness: CGFloat = 300
|
|
||||||
private let presentDamping: CGFloat = 20
|
|
||||||
private let dismissStiffness: CGFloat = 200
|
|
||||||
private let dismissDamping: CGFloat = 20
|
|
||||||
|
|
||||||
public class MatchedGeometryState: ObservableObject {
|
|
||||||
@Published var presentationID: AnyHashable?
|
|
||||||
@Published var animating: Bool = false
|
|
||||||
@Published public var mode: Mode = .presenting
|
|
||||||
@Published var sources: [SourceKey: (() -> AnyView, CGRect)] = [:]
|
|
||||||
@Published var currentFrames: [AnyHashable: CGRect] = [:]
|
|
||||||
@Published var destinations: [AnyHashable: (AnyView, CGRect)] = [:]
|
|
||||||
|
|
||||||
public enum Mode: Equatable {
|
|
||||||
case presenting
|
|
||||||
case idle
|
|
||||||
case dismissing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class MatchedGeometryViewController<Content: View>: UIViewController, UIViewControllerTransitioningDelegate {
|
|
||||||
|
|
||||||
let presentationID: AnyHashable
|
|
||||||
let content: Content
|
|
||||||
let state: MatchedGeometryState
|
|
||||||
let backgroundColor: UIColor
|
|
||||||
var contentHost: UIHostingController<ContentContainerView>!
|
|
||||||
var matchedHost: UIHostingController<MatchedContainerView>!
|
|
||||||
|
|
||||||
init(presentationID: AnyHashable, content: Content, state: MatchedGeometryState, backgroundColor: UIColor) {
|
|
||||||
self.presentationID = presentationID
|
|
||||||
self.content = content
|
|
||||||
self.state = state
|
|
||||||
self.backgroundColor = backgroundColor
|
|
||||||
|
|
||||||
super.init(nibName: nil, bundle: nil)
|
|
||||||
|
|
||||||
modalPresentationStyle = .custom
|
|
||||||
transitioningDelegate = self
|
|
||||||
}
|
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
|
||||||
fatalError("init(coder:) has not been implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
override func viewDidLoad() {
|
|
||||||
super.viewDidLoad()
|
|
||||||
|
|
||||||
contentHost = UIHostingController(rootView: ContentContainerView(content: content, state: state))
|
|
||||||
contentHost.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
|
||||||
contentHost.view.frame = view.bounds
|
|
||||||
contentHost.view.backgroundColor = backgroundColor
|
|
||||||
addChild(contentHost)
|
|
||||||
view.addSubview(contentHost.view)
|
|
||||||
contentHost.didMove(toParent: self)
|
|
||||||
}
|
|
||||||
|
|
||||||
override func viewWillAppear(_ animated: Bool) {
|
|
||||||
super.viewWillAppear(animated)
|
|
||||||
|
|
||||||
state.presentationID = presentationID
|
|
||||||
}
|
|
||||||
|
|
||||||
var currentPresentationSources: [AnyHashable: (() -> AnyView, CGRect)] {
|
|
||||||
Dictionary(uniqueKeysWithValues: state.sources.filter { $0.key.presentationID == presentationID }.map { ($0.key.matchedID, $0.value) })
|
|
||||||
}
|
|
||||||
|
|
||||||
func addMatchedHostingController() {
|
|
||||||
let sources = currentPresentationSources.map { (id: $0.key, view: $0.value.0) }
|
|
||||||
matchedHost = UIHostingController(rootView: MatchedContainerView(sources: sources, state: state))
|
|
||||||
matchedHost.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
|
||||||
matchedHost.view.frame = view.bounds
|
|
||||||
matchedHost.view.backgroundColor = .clear
|
|
||||||
matchedHost.view.layer.zPosition = 100
|
|
||||||
addChild(matchedHost)
|
|
||||||
view.addSubview(matchedHost.view)
|
|
||||||
matchedHost.didMove(toParent: self)
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ContentContainerView: View {
|
|
||||||
let content: Content
|
|
||||||
let state: MatchedGeometryState
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
content
|
|
||||||
.environmentObject(state)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct MatchedContainerView: View {
|
|
||||||
let sources: [(id: AnyHashable, view: () -> AnyView)]
|
|
||||||
@ObservedObject var state: MatchedGeometryState
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
ZStack {
|
|
||||||
ForEach(sources, id: \.id) { (id, view) in
|
|
||||||
matchedView(id: id, source: view)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ViewBuilder
|
|
||||||
func matchedView(id: AnyHashable, source: () -> AnyView) -> some View {
|
|
||||||
if let frame = state.currentFrames[id],
|
|
||||||
let dest = state.destinations[id]?.0 {
|
|
||||||
ZStack {
|
|
||||||
source()
|
|
||||||
dest
|
|
||||||
.opacity(state.mode == .presenting ? (state.animating ? 1 : 0) : (state.animating ? 0 : 1))
|
|
||||||
}
|
|
||||||
.frame(width: frame.width, height: frame.height)
|
|
||||||
.position(x: frame.midX, y: frame.midY)
|
|
||||||
.ignoresSafeArea()
|
|
||||||
.animation(.interpolatingSpring(mass: Double(mass), stiffness: Double(state.mode == .presenting ? presentStiffness : dismissStiffness), damping: Double(state.mode == .presenting ? presentDamping : dismissDamping), initialVelocity: 0), value: frame)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: UIViewControllerTransitioningDelegate
|
|
||||||
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
|
|
||||||
return MatchedGeometryPresentationAnimationController<Content>()
|
|
||||||
}
|
|
||||||
|
|
||||||
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
|
|
||||||
return MatchedGeometryDismissAnimationController<Content>()
|
|
||||||
}
|
|
||||||
|
|
||||||
func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
|
|
||||||
return MatchedGeometryPresentationController(presentedViewController: presented, presenting: presenting)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
class MatchedGeometryPresentationAnimationController<Content: View>: NSObject, UIViewControllerAnimatedTransitioning {
|
|
||||||
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
|
|
||||||
return 0.8
|
|
||||||
}
|
|
||||||
|
|
||||||
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
|
|
||||||
let matchedGeomVC = transitionContext.viewController(forKey: .to) as! MatchedGeometryViewController<Content>
|
|
||||||
let container = transitionContext.containerView
|
|
||||||
|
|
||||||
// add the VC to the container, which kicks off layout out the content hosting controller
|
|
||||||
matchedGeomVC.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
|
||||||
matchedGeomVC.view.frame = container.bounds
|
|
||||||
container.addSubview(matchedGeomVC.view)
|
|
||||||
|
|
||||||
// layout out the content hosting controller and having enough destinations may take a while
|
|
||||||
// so listen for when it's ready, rather than trying to guess at the timing
|
|
||||||
let cancellable = matchedGeomVC.state.$destinations
|
|
||||||
.filter { destinations in matchedGeomVC.currentPresentationSources.allSatisfy { source in destinations.keys.contains(source.key) } }
|
|
||||||
.first()
|
|
||||||
.sink { destinations in
|
|
||||||
matchedGeomVC.addMatchedHostingController()
|
|
||||||
|
|
||||||
// setup the initial state for the animation
|
|
||||||
matchedGeomVC.matchedHost.view.isHidden = true
|
|
||||||
matchedGeomVC.state.mode = .presenting
|
|
||||||
matchedGeomVC.state.currentFrames = matchedGeomVC.currentPresentationSources.mapValues(\.1)
|
|
||||||
|
|
||||||
// wait one runloop iteration for the matched hosting controller to be setup
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
matchedGeomVC.matchedHost.view.isHidden = false
|
|
||||||
matchedGeomVC.state.animating = true
|
|
||||||
// get the now-current destinations, in case they've changed since the sunk value was published
|
|
||||||
matchedGeomVC.state.currentFrames = matchedGeomVC.state.destinations.mapValues(\.1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
matchedGeomVC.contentHost.view.layer.opacity = 0
|
|
||||||
let spring = UISpringTimingParameters(mass: mass, stiffness: presentStiffness, damping: presentDamping, initialVelocity: .zero)
|
|
||||||
let animator = UIViewPropertyAnimator(duration: self.transitionDuration(using: transitionContext), timingParameters: spring)
|
|
||||||
animator.addAnimations {
|
|
||||||
matchedGeomVC.contentHost.view.layer.opacity = 1
|
|
||||||
}
|
|
||||||
animator.addCompletion { _ in
|
|
||||||
transitionContext.completeTransition(true)
|
|
||||||
matchedGeomVC.state.animating = false
|
|
||||||
matchedGeomVC.state.mode = .idle
|
|
||||||
|
|
||||||
matchedGeomVC.matchedHost?.view.removeFromSuperview()
|
|
||||||
matchedGeomVC.matchedHost?.removeFromParent()
|
|
||||||
cancellable.cancel()
|
|
||||||
}
|
|
||||||
animator.startAnimation()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class MatchedGeometryDismissAnimationController<Content: View>: NSObject, UIViewControllerAnimatedTransitioning {
|
|
||||||
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
|
|
||||||
return 0.8
|
|
||||||
}
|
|
||||||
|
|
||||||
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
|
|
||||||
let matchedGeomVC = transitionContext.viewController(forKey: .from) as! MatchedGeometryViewController<Content>
|
|
||||||
|
|
||||||
// recreate the matched host b/c using the current destinations doesn't seem to update the existing one
|
|
||||||
matchedGeomVC.addMatchedHostingController()
|
|
||||||
matchedGeomVC.matchedHost.view.isHidden = true
|
|
||||||
matchedGeomVC.state.mode = .dismissing
|
|
||||||
matchedGeomVC.state.currentFrames = matchedGeomVC.state.destinations.mapValues(\.1)
|
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
matchedGeomVC.matchedHost.view.isHidden = false
|
|
||||||
matchedGeomVC.state.animating = true
|
|
||||||
matchedGeomVC.state.currentFrames = matchedGeomVC.currentPresentationSources.mapValues(\.1)
|
|
||||||
}
|
|
||||||
|
|
||||||
let spring = UISpringTimingParameters(mass: mass, stiffness: dismissStiffness, damping: dismissDamping, initialVelocity: .zero)
|
|
||||||
let animator = UIViewPropertyAnimator(duration: transitionDuration(using: transitionContext), timingParameters: spring)
|
|
||||||
animator.addAnimations {
|
|
||||||
matchedGeomVC.contentHost.view.layer.opacity = 0
|
|
||||||
}
|
|
||||||
animator.addCompletion { _ in
|
|
||||||
transitionContext.completeTransition(true)
|
|
||||||
matchedGeomVC.state.animating = false
|
|
||||||
matchedGeomVC.state.mode = .idle
|
|
||||||
}
|
|
||||||
animator.startAnimation()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class MatchedGeometryPresentationController: UIPresentationController {
|
|
||||||
override func dismissalTransitionWillBegin() {
|
|
||||||
super.dismissalTransitionWillBegin()
|
|
||||||
delegate?.presentationControllerWillDismiss?(self)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,62 +0,0 @@
|
|||||||
//
|
|
||||||
// View+PresentViewController.swift
|
|
||||||
// MatchGeom
|
|
||||||
//
|
|
||||||
// Created by Shadowfacts on 4/24/23.
|
|
||||||
//
|
|
||||||
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
extension View {
|
|
||||||
func presentViewController(_ makeVC: @escaping () -> UIViewController, isPresented: Binding<Bool>) -> some View {
|
|
||||||
self
|
|
||||||
.background(
|
|
||||||
ViewControllerPresenter(makeVC: makeVC, isPresented: isPresented)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private struct ViewControllerPresenter: UIViewControllerRepresentable {
|
|
||||||
let makeVC: () -> UIViewController
|
|
||||||
@Binding var isPresented: Bool
|
|
||||||
|
|
||||||
func makeUIViewController(context: Context) -> UIViewController {
|
|
||||||
return UIViewController()
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
|
|
||||||
if isPresented {
|
|
||||||
if uiViewController.presentedViewController == nil {
|
|
||||||
let presented = makeVC()
|
|
||||||
presented.presentationController!.delegate = context.coordinator
|
|
||||||
uiViewController.present(presented, animated: true)
|
|
||||||
context.coordinator.didPresent = true
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if context.coordinator.didPresent,
|
|
||||||
let presentedViewController = uiViewController.presentedViewController,
|
|
||||||
!presentedViewController.isBeingDismissed {
|
|
||||||
uiViewController.dismiss(animated: true)
|
|
||||||
context.coordinator.didPresent = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeCoordinator() -> Coordinator {
|
|
||||||
return Coordinator(isPresented: $isPresented)
|
|
||||||
}
|
|
||||||
|
|
||||||
class Coordinator: NSObject, UIAdaptivePresentationControllerDelegate {
|
|
||||||
@Binding var isPresented: Bool
|
|
||||||
var didPresent = false
|
|
||||||
|
|
||||||
init(isPresented: Binding<Bool>) {
|
|
||||||
self._isPresented = isPresented
|
|
||||||
}
|
|
||||||
|
|
||||||
func presentationControllerWillDismiss(_ presentationController: UIPresentationController) {
|
|
||||||
isPresented = false
|
|
||||||
didPresent = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -685,7 +685,6 @@
|
|||||||
D6A6C11425B62E9700298D0F /* CacheExpiry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CacheExpiry.swift; sourceTree = "<group>"; };
|
D6A6C11425B62E9700298D0F /* CacheExpiry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CacheExpiry.swift; sourceTree = "<group>"; };
|
||||||
D6A6C11A25B63CEE00298D0F /* MemoryCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemoryCache.swift; sourceTree = "<group>"; };
|
D6A6C11A25B63CEE00298D0F /* MemoryCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemoryCache.swift; sourceTree = "<group>"; };
|
||||||
D6A8D7A42C14DB280007B285 /* PersistentHistoryTokenStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PersistentHistoryTokenStore.swift; sourceTree = "<group>"; };
|
D6A8D7A42C14DB280007B285 /* PersistentHistoryTokenStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PersistentHistoryTokenStore.swift; sourceTree = "<group>"; };
|
||||||
D6A9E04F29F8917500BEDC7E /* MatchedGeometryPresentation */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = MatchedGeometryPresentation; sourceTree = "<group>"; };
|
|
||||||
D6AC956623C4347E008C9946 /* MainSceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainSceneDelegate.swift; sourceTree = "<group>"; };
|
D6AC956623C4347E008C9946 /* MainSceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainSceneDelegate.swift; sourceTree = "<group>"; };
|
||||||
D6ADB6E728E8C878009924AB /* PublicTimelineDescriptionCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PublicTimelineDescriptionCollectionViewCell.swift; sourceTree = "<group>"; };
|
D6ADB6E728E8C878009924AB /* PublicTimelineDescriptionCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PublicTimelineDescriptionCollectionViewCell.swift; sourceTree = "<group>"; };
|
||||||
D6ADB6E928E91C30009924AB /* TimelineStatusCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineStatusCollectionViewCell.swift; sourceTree = "<group>"; };
|
D6ADB6E928E91C30009924AB /* TimelineStatusCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineStatusCollectionViewCell.swift; sourceTree = "<group>"; };
|
||||||
@ -1241,7 +1240,6 @@
|
|||||||
D6BD395C29B789D5005FFD2B /* TuskerComponents */,
|
D6BD395C29B789D5005FFD2B /* TuskerComponents */,
|
||||||
D6BD395729B6441F005FFD2B /* ComposeUI */,
|
D6BD395729B6441F005FFD2B /* ComposeUI */,
|
||||||
D6CA6ED029EF6060003EC5DF /* TuskerPreferences */,
|
D6CA6ED029EF6060003EC5DF /* TuskerPreferences */,
|
||||||
D6A9E04F29F8917500BEDC7E /* MatchedGeometryPresentation */,
|
|
||||||
D642E83D2BA7AD0F004BFD6A /* GalleryVC */,
|
D642E83D2BA7AD0F004BFD6A /* GalleryVC */,
|
||||||
D65A26242BC39A02005EB5D8 /* PushNotifications */,
|
D65A26242BC39A02005EB5D8 /* PushNotifications */,
|
||||||
);
|
);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user