diff --git a/ElementX/Sources/FlowCoordinators/MediaEventsTimelineFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/MediaEventsTimelineFlowCoordinator.swift index a08af5a6a6..bcb14f3e99 100644 --- a/ElementX/Sources/FlowCoordinators/MediaEventsTimelineFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/MediaEventsTimelineFlowCoordinator.swift @@ -126,8 +126,6 @@ class MediaEventsTimelineFlowCoordinator: FlowCoordinatorProtocol { } .store(in: &cancellables) - navigationStackCoordinator.setFullScreenCoverCoordinator(coordinator) { - previewContext.completion?() - } + navigationStackCoordinator.setFullScreenCoverCoordinator(coordinator) } } diff --git a/ElementX/Sources/Screens/FilePreviewScreen/TimelineMediaPreviewCoordinator.swift b/ElementX/Sources/Screens/FilePreviewScreen/TimelineMediaPreviewCoordinator.swift index 5f937f1805..2fef50976f 100644 --- a/ElementX/Sources/Screens/FilePreviewScreen/TimelineMediaPreviewCoordinator.swift +++ b/ElementX/Sources/Screens/FilePreviewScreen/TimelineMediaPreviewCoordinator.swift @@ -72,6 +72,9 @@ final class TimelineMediaPreviewCoordinator: CoordinatorProtocol { } func toPresentable() -> AnyView { - AnyView(TimelineMediaPreviewScreen(context: viewModel.context)) + // Calling the completion onDisappear isn't ideal, but we don't push away from the screen so it should be + // a good enough approximation of didDismiss, given that the only other option is our navigation callbacks + // which are essentially willDismiss callbacks and happen too early for this particular completion handler. + AnyView(TimelineMediaPreviewScreen(context: viewModel.context, onDisappear: parameters.context.completion)) } } diff --git a/ElementX/Sources/Screens/FilePreviewScreen/View/TimelineMediaPreviewScreen.swift b/ElementX/Sources/Screens/FilePreviewScreen/View/TimelineMediaPreviewScreen.swift index aaac9d7e36..b6196490aa 100644 --- a/ElementX/Sources/Screens/FilePreviewScreen/View/TimelineMediaPreviewScreen.swift +++ b/ElementX/Sources/Screens/FilePreviewScreen/View/TimelineMediaPreviewScreen.swift @@ -12,13 +12,15 @@ import SwiftUI struct TimelineMediaPreviewScreen: View { @ObservedObject var context: TimelineMediaPreviewViewModel.Context + var onDisappear: (() -> Void)? private var currentItem: TimelineMediaPreviewItem { context.viewState.currentItem } var body: some View { NavigationStack { - Color.clear - .overlay { QuickLookView(viewModelContext: context).ignoresSafeArea() } // Overlay to stop QL hijacking the toolbar. + Color.black + .opacity(0.01) // A completely clear view breaks any SwiftUI gestures (such as drag to dismiss). + .background { QuickLookView(viewModelContext: context).ignoresSafeArea() } // Not the root view to stop QL hijacking the toolbar. .toolbar { toolbar } .toolbarBackground(.visible, for: .navigationBar) // The toolbar's scrollEdgeAppearance isn't aware of the quicklook view 🤷‍♂️ .toolbarBackground(.visible, for: .bottomBar) @@ -39,6 +41,9 @@ struct TimelineMediaPreviewScreen: View { } .alert(item: $context.alertInfo) .preferredColorScheme(.dark) + .onDisappear { + onDisappear?() + } .zoomTransition(sourceID: currentItem.id, in: context.viewState.transitionNamespace) } @@ -114,6 +119,8 @@ struct TimelineMediaPreviewScreen: View { } } +// MARK: - QuickLook + private struct QuickLookView: UIViewControllerRepresentable { let viewModelContext: TimelineMediaPreviewViewModel.Context @@ -128,6 +135,8 @@ private struct QuickLookView: UIViewControllerRepresentable { Coordinator(viewModelContext: viewModelContext) } + // MARK: Coordinator + class Coordinator: NSObject, QLPreviewControllerDataSource, QLPreviewControllerDelegate { private let viewModelContext: TimelineMediaPreviewViewModel.Context @@ -148,6 +157,8 @@ private struct QuickLookView: UIViewControllerRepresentable { } } + // MARK: UIKit + class PreviewController: QLPreviewController { let coordinator: Coordinator