Post

Replies

Boosts

Views

Activity

SwiftUI Tap Gestures Not Responding in SwiftUI View After UIKit Interactive Transition Cancelled
I am using a custom UINavigationController with a custom pop animation and an interactive transition. The custom navigation controller works well for standard UIKit view controllers. However, when I push a UIHostingController that hosts a SwiftUI view, the SwiftUI view becomes unresponsive to tap gestures if the interactive transition is cancelled. The issue seems to occur specifically after the interactive transition is started and then cancelled. This is interactive transition code class CustomPopAnimator: NSObject, UIViewControllerAnimatedTransitioning { func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { return 0.3 } func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { let containerView = transitionContext.containerView guard let fromView = transitionContext.view(forKey: .from), let toView = transitionContext.view(forKey: .to) else { return } containerView.insertSubview(toView, belowSubview: fromView) let screenWidth = UIScreen.main.bounds.width toView.transform = CGAffineTransform(translationX: -screenWidth / 3, y: 0) UIView.animate(withDuration: transitionDuration(using: transitionContext), animations: { fromView.transform = CGAffineTransform(translationX: screenWidth, y: 0) toView.transform = .identity }) { finished in fromView.transform = .identity toView.transform = .identity fromView.isUserInteractionEnabled = true transitionContext.completeTransition(!transitionContext.transitionWasCancelled) } } } class CustomInteractiveTransition: UIPercentDrivenInteractiveTransition { var hasStarted = false var shouldFinish = false override func cancel() { super.cancel() reset() } override func finish() { super.finish() reset() } private func reset() { hasStarted = false shouldFinish = false } } @objc class CustomNavigationController: UINavigationController, UINavigationControllerDelegate { private let customAnimator = CustomPopAnimator() private let customInteractiveTransition = CustomInteractiveTransition() override func viewDidLoad() { super.viewDidLoad() delegate = self setupGesture() } private func setupGesture() { let edgeSwipeGesture = UIScreenEdgePanGestureRecognizer(target: self, action: #selector(handleEdgeSwipe(_:))) edgeSwipeGesture.edges = .left view.addGestureRecognizer(edgeSwipeGesture) } @objc private func handleEdgeSwipe(_ gesture: UIScreenEdgePanGestureRecognizer) { let translation = gesture.translation(in: view) let progress = translation.x / view.bounds.width switch gesture.state { case .began: customInteractiveTransition.hasStarted = true popViewController(animated: true) case .changed: customInteractiveTransition.shouldFinish = progress > 0.5 customInteractiveTransition.update(progress) case .ended: customInteractiveTransition.hasStarted = false customInteractiveTransition.shouldFinish ? customInteractiveTransition.finish() : customInteractiveTransition.cancel() case .cancelled: customInteractiveTransition.hasStarted = false customInteractiveTransition.cancel() default: break } } func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationController.Operation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? { return operation == .pop ? customAnimator : nil } func navigationController(_ navigationController: UINavigationController, interactionControllerFor animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? { return customInteractiveTransition.hasStarted ? customInteractiveTransition : nil } func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) { if let hostingController = viewController as? AIMAHostingController<SwiftUIView> { DispatchQueue.main.async { hostingController.view.setNeedsLayout() hostingController.view.layoutIfNeeded() } } } } SwiftUI code struct SwiftUIView: View { var body: some View { VStack { Spacer() Text("Hello, World!") .id("123") .background(Color.green) .padding() .onTapGesture { print("===onclick") } Spacer() } } } let hostingController = UIHostingController(rootView: SwiftUIView()) navigationController?.pushViewController(hostingController, animated: true)
1
1
612
Jun ’24