diff --git a/Nextcloud/Viewer/NCViewerNextcloudText/NCViewerNextcloudText.swift b/Nextcloud/Viewer/NCViewerNextcloudText/NCViewerNextcloudText.swift index bfdb114c..bcdb7685 100644 --- a/Nextcloud/Viewer/NCViewerNextcloudText/NCViewerNextcloudText.swift +++ b/Nextcloud/Viewer/NCViewerNextcloudText/NCViewerNextcloudText.swift @@ -34,6 +34,12 @@ class NCViewerNextcloudText: UIViewController, WKNavigationDelegate, WKScriptMes var editor: String = "" var fileName: String? let hud = JGProgressHUD() + private lazy var backSwipeGestureRecognizer: UIScreenEdgePanGestureRecognizer = { + let gestureRecognizer = UIScreenEdgePanGestureRecognizer(target: self, action: #selector(handleBackSwipe(_:))) + gestureRecognizer.edges = .left + gestureRecognizer.cancelsTouchesInView = false + return gestureRecognizer + }() // MARK: - View Life Cycle @@ -43,6 +49,7 @@ class NCViewerNextcloudText: UIViewController, WKNavigationDelegate, WKScriptMes override func viewDidLoad() { super.viewDidLoad() + view.addGestureRecognizer(backSwipeGestureRecognizer) // navigationController?.setNavigationBarHidden(true, animated: true) navigationItem.title = fileName @@ -91,8 +98,17 @@ class NCViewerNextcloudText: UIViewController, WKNavigationDelegate, WKScriptMes } @objc func viewUnload() { - self.dismiss(animated: true) - // navigationController?.popViewController(animated: true) + dismiss(animated: true) + } + + @objc private func handleBackSwipe(_ gestureRecognizer: UIScreenEdgePanGestureRecognizer) { + guard gestureRecognizer.state == .ended else { return } + + let translation = gestureRecognizer.translation(in: view) + let velocity = gestureRecognizer.velocity(in: view) + guard translation.x > view.bounds.width * 0.15, velocity.x > 0 else { return } + + dismiss(animated: true) } // MARK: - NotificationCenter @@ -178,6 +194,69 @@ class NCViewerNextcloudText: UIViewController, WKNavigationDelegate, WKScriptMes } } +extension NCViewerNextcloudText: UIViewControllerTransitioningDelegate { + + func animationController(forPresented presented: UIViewController, + presenting: UIViewController, + source: UIViewController) -> (any UIViewControllerAnimatedTransitioning)? { + HorizontalSlideAnimator(isPresenting: true) + } + + func animationController(forDismissed dismissed: UIViewController) -> (any UIViewControllerAnimatedTransitioning)? { + HorizontalSlideAnimator(isPresenting: false) + } +} + +private final class HorizontalSlideAnimator: NSObject, UIViewControllerAnimatedTransitioning { + + private let isPresenting: Bool + + init(isPresenting: Bool) { + self.isPresenting = isPresenting + } + + func transitionDuration(using transitionContext: (any UIViewControllerContextTransitioning)?) -> TimeInterval { + 0.35 + } + + func animateTransition(using transitionContext: any UIViewControllerContextTransitioning) { + let containerView = transitionContext.containerView + guard let fromView = transitionContext.view(forKey: .from), + let toView = transitionContext.view(forKey: .to) else { + transitionContext.completeTransition(false) + return + } + + let width = containerView.bounds.width + let destinationView = isPresenting ? toView : fromView + let sourceView = isPresenting ? fromView : toView + + if isPresenting { + destinationView.frame = containerView.bounds.offsetBy(dx: width, dy: 0) + containerView.addSubview(destinationView) + } else { + containerView.insertSubview(sourceView, belowSubview: destinationView) + } + + UIView.animate(withDuration: transitionDuration(using: transitionContext), + delay: 0, + options: [.curveEaseOut]) { + destinationView.frame = self.isPresenting + ? containerView.bounds + : containerView.bounds.offsetBy(dx: width, dy: 0) + sourceView.frame = self.isPresenting + ? containerView.bounds.offsetBy(dx: -width * 0.25, dy: 0) + : containerView.bounds + } completion: { finished in + let completed = finished && !transitionContext.transitionWasCancelled + if !completed { + sourceView.frame = containerView.bounds + } + transitionContext.completeTransition(completed) + } + } +} + extension NCViewerNextcloudText: UINavigationControllerDelegate { override func didMove(toParent parent: UIViewController?) { diff --git a/Source/EditorViewController.swift b/Source/EditorViewController.swift index 5c86433c..479a0337 100644 --- a/Source/EditorViewController.swift +++ b/Source/EditorViewController.swift @@ -52,6 +52,12 @@ class EditorViewController: UIViewController { private var observers = [NSObjectProtocol]() private let throttler = Throttler(minimumDelay: 0.5) + private lazy var backSwipeGestureRecognizer: UIScreenEdgePanGestureRecognizer = { + let gestureRecognizer = UIScreenEdgePanGestureRecognizer(target: self, action: #selector(handleBackSwipe(_:))) + gestureRecognizer.edges = .left + gestureRecognizer.cancelsTouchesInView = false + return gestureRecognizer + }() var screenShot: UIImage { var capturedScreen: UIImage? @@ -72,6 +78,7 @@ class EditorViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() + view.addGestureRecognizer(backSwipeGestureRecognizer) view.addSubview(noteView) noteView.translatesAutoresizingMaskIntoConstraints = false noteView.delegate = self @@ -203,6 +210,21 @@ class EditorViewController: UIViewController { } } } + + @objc private func handleBackSwipe(_ gestureRecognizer: UIScreenEdgePanGestureRecognizer) { + guard gestureRecognizer.state == .ended else { return } + + let translation = gestureRecognizer.translation(in: view) + let velocity = gestureRecognizer.velocity(in: view) + guard translation.x > view.bounds.width * 0.15, velocity.x > 0 else { return } + + if let navigationController = navigationController, + navigationController.viewControllers.first !== self { + navigationController.popViewController(animated: true) + } else { + splitViewController?.show(.primary) + } + } // MARK: - Navigation diff --git a/Source/Screens/Notes/NotesTableViewController.swift b/Source/Screens/Notes/NotesTableViewController.swift index 1781f92a..2c81ff54 100644 --- a/Source/Screens/Notes/NotesTableViewController.swift +++ b/Source/Screens/Notes/NotesTableViewController.swift @@ -502,8 +502,9 @@ class NotesTableViewController: BaseUITableViewController, Logging, NSFetchedRes viewController.editor = "text" viewController.link = url viewController.fileName = note.title - viewController.modalPresentationStyle = .fullScreen - self.navigationController?.present(viewController, animated: true) + viewController.modalPresentationStyle = .custom + viewController.transitioningDelegate = viewController + self.present(viewController, animated: true) } else { let alert = UIAlertController(title: "Error", message: "Cannot open file for direct editing: \(error.localizedDescription)", preferredStyle: .alert) alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: "Default action"), style: .default))