Work-around issue with QLPreviewController not supporting swipe-to-dismiss and pinch-to-dismiss when presented from SwiftUI by creating a transparent UIViewController wrapper around QLPreviewController that presents it using UIKit instead. (#452) close #106

Co-authored-by: Greg <greg@cromulentlabs.com>
This commit is contained in:
Greg Gardner 2023-01-27 21:45:15 -08:00 committed by GitHub
parent be802980c5
commit b1b9e658c9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 76 additions and 53 deletions

View file

@ -53,9 +53,10 @@ struct IceCubesApp: App {
.environmentObject(theme)
.environmentObject(watcher)
.environmentObject(PushNotificationsService.shared)
.sheet(item: $quickLook.url, content: { url in
.fullScreenCover(item: $quickLook.url, content: { url in
QuickLookPreview(selectedURL: url, urls: quickLook.urls)
.edgesIgnoringSafeArea(.bottom)
.background(TransparentBackground())
})
}
.commands {

View file

@ -12,61 +12,81 @@ struct QuickLookPreview: UIViewControllerRepresentable {
let selectedURL: URL
let urls: [URL]
func makeUIViewController(context: Context) -> UINavigationController {
let controller = AppQLPreviewController()
controller.dataSource = context.coordinator
controller.delegate = context.coordinator
let nav = UINavigationController(rootViewController: controller)
return nav
func makeUIViewController(context: Context) -> UIViewController {
return AppQLPreviewController(selectedURL: selectedURL, urls: urls)
}
func updateUIViewController(
_: UINavigationController, context _: Context
_: UIViewController, context _: Context
) {}
}
func makeCoordinator() -> Coordinator {
return Coordinator(parent: self)
class AppQLPreviewController: UIViewController {
let selectedURL: URL
let urls: [URL]
var qlController : QLPreviewController?
init(selectedURL: URL, urls: [URL]) {
self.selectedURL = selectedURL
self.urls = urls
super.init(nibName: nil, bundle: nil)
}
class Coordinator: NSObject, QLPreviewControllerDataSource, QLPreviewControllerDelegate {
let parent: QuickLookPreview
init(parent: QuickLookPreview) {
self.parent = parent
}
func numberOfPreviewItems(in _: QLPreviewController) -> Int {
return parent.urls.count
}
func previewController(_: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
return parent.urls[index] as QLPreviewItem
}
func previewController(_: QLPreviewController, editingModeFor _: QLPreviewItem) -> QLPreviewItemEditingMode {
.createCopy
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if self.qlController == nil {
self.qlController = QLPreviewController()
self.qlController?.dataSource = self
self.qlController?.delegate = self
self.qlController?.currentPreviewItemIndex = urls.firstIndex(of: selectedURL) ?? 0
self.present(self.qlController!, animated: true)
}
}
}
class AppQLPreviewController: QLPreviewController {
private var closeButton: UIBarButtonItem {
.init(
title: NSLocalizedString("action.done", comment: ""),
style: .plain,
target: self,
action: #selector(onCloseButton)
)
extension AppQLPreviewController : QLPreviewControllerDataSource {
func numberOfPreviewItems(in _: QLPreviewController) -> Int {
return self.urls.count
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if UIDevice.current.userInterfaceIdiom != .pad {
navigationItem.rightBarButtonItem = closeButton
}
}
@objc private func onCloseButton() {
dismiss(animated: true)
func previewController(_: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
return self.urls[index] as QLPreviewItem
}
}
extension AppQLPreviewController : QLPreviewControllerDelegate {
func previewController(_: QLPreviewController, editingModeFor _: QLPreviewItem) -> QLPreviewItemEditingMode {
.createCopy
}
func previewControllerWillDismiss(_ controller: QLPreviewController) {
self.dismiss(animated: true)
}
}
struct TransparentBackground: UIViewControllerRepresentable {
public func makeUIViewController(context: Context) -> UIViewController {
return TransparentController()
}
public func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
}
class TransparentController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .clear
}
override func willMove(toParent parent: UIViewController?) {
super.willMove(toParent: parent)
parent?.view?.backgroundColor = .clear
parent?.modalPresentationStyle = .overCurrentContext
}
}
}

View file

@ -11,7 +11,9 @@ public class QuickLook: ObservableObject {
public init() {}
public func prepareFor(urls: [URL], selectedURL: URL) async {
withAnimation {
var transaction = Transaction(animation: .default)
transaction.disablesAnimations = true
withTransaction(transaction) {
isPreparing = true
}
do {
@ -27,18 +29,18 @@ public class QuickLook: ObservableObject {
}
return paths
})
self.urls = paths
url = paths.first(where: { $0.lastPathComponent == selectedURL.lastPathComponent })
withAnimation {
withTransaction(transaction) {
self.urls = paths
url = paths.first(where: { $0.lastPathComponent == selectedURL.lastPathComponent })
isPreparing = false
}
} catch {
withAnimation {
withTransaction(transaction) {
isPreparing = false
self.urls = []
url = nil
latestError = error
}
self.urls = []
url = nil
latestError = error
}
}