ios - 如何添加屏幕边缘平移手势识别器来处理此弹出动画?

标签 ios swift uinavigationcontroller

我对 UINavigationController 进行了子类化,并创建了一个非交互式动画对象来处理推送和弹出转换。要完成此操作,我如何添加 UIScreenEdgePanGestureRecognizer 来处理下面的自定义弹出动画?我对从这里去哪里感到困惑,所以任何帮助都会很棒,谢谢!

class CustomNavigationController: UINavigationController {

    override func viewDidLoad() {
        super.viewDidLoad()

        delegate = self
        isNavigationBarHidden = true

    }

}

extension CustomNavigationController: UINavigationControllerDelegate {

    // noninteractive animator objects
    func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {

        switch operation {

        case .push:
            return CustomPushAnimator()

        case .pop:
            return CustomPopAnimator()

        default:
            return nil

        }

    }

    // the interactive animator controller
    func navigationController(_ navigationController: UINavigationController, interactionControllerFor animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {

        switch animationController {

        case is CustomPopAnimator:
            return CustomInteractiveController()

        default:
            return nil

    }

}

// the noninteractive push animator
class CustomPushAnimator: NSObject, UIViewControllerAnimatedTransitioning {

    // push transition duration
    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {

        return 0.2

    }

    // push transition animation
    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {

        let viewWidth = UIScreen.main.bounds.width
        let viewHeight = UIScreen.main.bounds.height
        let containerView = transitionContext.containerView
        let toViewController = transitionContext.viewController(forKey: .to)

        containerView.addSubview((toViewController?.view)!)
        toViewController?.view.frame = CGRect(x: viewWidth, y: 0, width: viewWidth, height: viewHeight)

        UIView.animate(withDuration: transitionDuration(using: transitionContext), delay: 0, options: UIViewAnimationOptions.curveEaseInOut, animations: {
            toViewController?.view.frame = CGRect(x: 0, y: 0, width: viewWidth, height: viewHeight)
        }, completion: { finished in
            transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
        })

    }

}

// the noninteractive pop animator
class CustomPopAnimator: NSObject, UIViewControllerAnimatedTransitioning {

    // pop transition duration
    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {

        return 0.2

    }

    // pop transition animation
    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {

        let viewWidth = UIScreen.main.bounds.width
        let viewHeight = UIScreen.main.bounds.height
        let containerView = transitionContext.containerView
        let fromViewController = transitionContext.viewController(forKey: .from)
        let toViewController = transitionContext.viewController(forKey: .to)

        containerView.insertSubview((toViewController?.view)!, belowSubview: (fromViewController?.view)!)

        UIView.animate(withDuration: transitionDuration(using: transitionContext), delay: 0, options: UIViewAnimationOptions.curveEaseInOut, animations: {
            fromViewController?.view.frame = CGRect(x: viewWidth, y: 0, width: viewWidth, height: viewHeight)
        }, completion: { finished in
            fromViewController?.view.removeFromSuperview()
            transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
        })

    }

}

class CustomInteractiveController: NSObject, UIViewControllerInteractiveTransitioning {

    func startInteractiveTransition(_ transitionContext: UIViewControllerContextTransitioning) {

        // I think the edge screen pan gesture goes here? Again, any advice is greatly appreciated!

    }

}

最佳答案

一般来说,编写交互式自定义转场动画的流程是:

  1. 在转换开始之前,您必须给负责转换的 View Controller 一个委托(delegate)。

  2. 您将拥有一个跟踪交互式手势的手势识别器。当手势识别器识别到时,它会触发到新 View Controller 的转换。

  3. 随着转换开始,代理将被要求提供动画 Controller 。您将返回一个 UIViewControllerAnimatedTransitioning 对象。

  4. 代理还将被要求提供交互 Controller 。您将返回一个 UIViewControllerInteractiveTransitioning 对象(或 nil 以防止转换是交互的)。此对象实现了 startInteractiveTransition(_:)

  5. 手势识别器通过在转换上下文中不断调用 updateInteractiveTransition(_:) 以及管理动画帧来继续。

  6. 手势迟早会结束。此时,我们必须决定是声明转换完成还是取消。一种典型的方法是说,如果用户执行了整个手势的一半以上,则构成完成;否则,即构成取消。我们相应地完成动画。

  7. 动画现在完成了,它的完成函数被调用了。我们必须调用转换上下文的 finishInteractiveTransitioncancelInteractiveTransition,然后调用它的 completeTransition(_:) 并带有一个参数,说明转换是否完成或已取消。

  8. 我们的 animationEnded 被调用,我们清理。

在过去,你可以使用 UIPercentDrivenInteractiveTransition 对象来帮助你,但现在我总是使用 UIViewPropertyAnimator 来实现我的自定义过渡动画(iOS 10 及更高版本);您必须首先重写整个代码才能做到这一点。我通常保留三个实例属性:UIViewPropertyAnimator、转换上下文(因为手势识别器需要引用)和一个 Bool 标志,表示这是否是交互式动画(因为相同的代码可以重复用于相同的交互式和非交互式版本动画)。

关于ios - 如何添加屏幕边缘平移手势识别器来处理此弹出动画?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46029011/

相关文章:

ios - 注销并清除当前 VC 以下的 VC

swift - 将 SKSpriteNode 旋转到另一个 SKSpriteNode

ios - 向 UIImagepickercontroller 导航栏添加后退按钮

ios - 使用 Alamofire Swift 3 上传图像 (Base64)

iphone - 当我从 iPhone 启动图像移动到与 UIView 上的背景相同的图像时,为什么顶部会引入边距?

iphone - 使用 UITabBarController 和 UINavigationController 的最佳方式

ios - 拖动 tableview 顶部时关闭 View 的更好方法?

ios - CCBlade 不适用于 cocos2d-x 3.8 版本

ios - 没有调用 Appirater 方法

c++ - box2d:在 ResetMassData 上获取 SIGABRT,b2Assert(m_I > 0.0f) - 中心惯性、质心