源码Github地址
- 系统模态跳转
//
open func present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil)
// 动画风格
public enum UIModalTransitionStyle : Int {
// 默认,从底部划入
case coverVertical
// 切换正反面效果
case flipHorizontal
// 渐变效果
case crossDissolve
@available(iOS 3.2, *)
// 翻页效果
case partialCurl
}
- 模态跳转退回上一层控制器
open func dismiss(animated flag: Bool, completion: (() -> Void)? = nil)
- push动画转场,支持右滑返回,当前控制器不是navigationController的子控制器时无效
open func pushViewController(_ viewController: UIViewController, animated: Bool)
- push动画转场退回上一层控制器
open func popViewController(animated: Bool) -> UIViewController?
- push动画转场退回根控制器
open func popToRootViewController(animated: Bool) -> [UIViewController]?
- push动画转场退回指定控制器
open func popToViewController(_ viewController: UIViewController, animated: Bool) -> [UIViewController]?
以上转场动画为系统提供,可以满足我们大部分使用场景,但是有时候我们可能会遇到特殊的需求,需要我们自定义转场动画
/// 创建一个动画管理类,这个类可以作为所有自定义转场动画的基类
class ZQBaseAninatedTranistion: UIPercentDrivenInteractiveTransition, UIViewControllerAnimatedTransitioning {
/**动画时长*/
var animateDuration:TimeInterval = 0.5
/**判断是否已经弹出*/
var isPopup:Bool = false
/**交互状态*/
var isInteraction:Bool = false
/**转场上下文*/
var context:UIViewControllerContextTransitioning?
/**视图*/
var containerView:UIView?
/**当前view*/
var fromView:UIView?
/**目标view*/
var toView:UIView?
/**当前控制器*/
var fromViewController:UIViewController?
/**目标控制器*/
var toViewController:UIViewController?
/// 设置动画时长
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
context = transitionContext
return animateDuration
}
/// 执行动画过程
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
context = transitionContext
//容器
containerView = transitionContext.containerView
//目标控制器
fromViewController = transitionContext.viewController(forKey: .from)
toViewController = transitionContext.viewController(forKey: .to)
fromView = fromViewController?.view
toView = toViewController?.view
isPopup ? dismiss():present()
}
/// 弹出转场动画写在这里
func present(){
}
/// 退回转场动画写在这里
func dismiss(){
}
}
实现从微信进入小程序的转场动画及右滑返回动画
class ZQFullCoverAnimatedTranistion: ZQBaseAninatedTranistion {
//最大偏移位置
var maxOffset:CGFloat = 0.0
//最右临界值
var rightCritcal:CGFloat = 20.0
//手势
var leftPanGesture:UIScreenEdgePanGestureRecognizer?
/// 启用边缘滑动返回
func usingLeftSwipDismiss(view:UIView){
//监听边缘滑动
leftPanGesture = UIScreenEdgePanGestureRecognizer(target: self, action: #selector(self.onEdgeSlide))
//左侧边缘滑动
leftPanGesture!.edges = .left
//添加事件
view.addGestureRecognizer(leftPanGesture!)
}
/// 当边缘滑动
@objc func onEdgeSlide(reco:UIScreenEdgePanGestureRecognizer){
let point = reco.location(in: reco.view)
//执行动画转场
let progress = point.x / reco.view!.bounds.width
if reco.state == .began {
//变更状态
isInteraction = true
//当开始,复位
maxOffset = 0.0
//---------这行很关键-----------
fromViewController?.dismiss(animated: true, completion: nil)
} else if reco.state == .changed {
maxOffset = max(point.x, maxOffset)
update(progress)
} else {
//当结束后
if point.x >= maxOffset && point.x > rightCritcal {
//完成退出
finish()
}else {
//取消后恢复
cancel()
}
//变更状态
isInteraction = false
}
}
/// 弹出动画,你可以在这这个方法里面实现你想要的任意动画
override func present(){
//目标视图
let toFrame = context!.finalFrame(for: toViewController!)
//添加视图
containerView!.addSubview(toView!)
let rect = containerView!.bounds
//设置动画
toView!.frame = CGRect(x: 0, y: rect.height * 2, width: rect.width, height: rect.height)
UIView.animate(withDuration: animateDuration, animations: { [weak self] in
self!.toView!.frame.origin.y = 0
self!.fromView!.transform = CGAffineTransform(scaleX: 0.95, y: 0.95)
}) {[weak self] (r) in
if self!.context!.transitionWasCancelled {
//操作失败了
self!.context!.completeTransition(false)
//移除视图
self!.toView?.removeFromSuperview()
}else{
self!.isPopup = true
self!.toView?.frame = toFrame
self!.context!.completeTransition(true)
}
}
}
/// 弹回动画,你可以在这这个方法里面实现你想要的任意动画
override func dismiss(){
//添加toView到底部一层
containerView!.addSubview(toView!)
containerView!.sendSubviewToBack(toView!)
//添加视图
let rect = containerView!.bounds
//设置动画
UIView.animate(withDuration: animateDuration, animations: { [weak self] in
self!.fromView?.frame.origin.y = rect.height * 2
self!.toView?.transform = .identity
}) { [weak self] (r) in
//取消
if self!.context!.transitionWasCancelled {
//操作失败
self!.context?.completeTransition(false)
self!.toView?.removeFromSuperview()
}else{
self!.isPopup = false
//将原始视图移除
self!.fromView?.removeFromSuperview()
//通知系统是否被取消,用于记录动画是否完成
self!.context!.completeTransition(true)
//移除手势
if self!.leftPanGesture != nil && self!.fromView != nil{
self!.fromView?.removeGestureRecognizer(self!.leftPanGesture!)
}
}
}
}
}
调用
class ViewController: UIViewController {
// 动画
let tranistionHandler = ZQFullCoverAnimatedTranistion()
override func viewDidLoad() {
super.viewDidLoad()
}
override func touchesBegan(_ touches: Set, with event: UIEvent?) {
let vc = ZQFullCoverToViewController()
/// 设置动画代理
vc.transitioningDelegate = self
// 开启右滑返回
tranistionHandler.usingLeftSwipDismiss(view: vc.view)
present(vc, animated: true, completion: nil)
}
}
extension ViewController:UIViewControllerTransitioningDelegate{
/// 弹出
///
/// - Parameters:
/// - presented: 目标
/// - presenting: 当前
/// - source: 资源
/// - Returns: 自定义的动画
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return tranistionHandler
}
/// 收起
///
/// - Parameter dismissed: 目标
/// - Returns: 自定义的动画
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return tranistionHandler
}
/// 响应手势操作
///
/// - Parameter animator: tranistionHandler
/// - Returns: 自定义的动画
func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
//避免手势与显示调用冲突,导致无法dismiss
if !tranistionHandler.isInteraction {
return nil
}
return tranistionHandler
}
}
源码Github地址
如果有帮到您,请点个喜欢