iOS 8的PresentationController

发布于:2014-07-07 11:16阅读数:

iOS 8新加入一个类:UIPresentationController,它与iOS 7新添加的几个类与协议一道,帮助我们方便快捷地实现ViewController的自定义过渡效果。我发现要搞懂一个新的API,最快的方法还是写一个例子。

(本文转自nonomori的博客,英文原文:iOS8 presentation controllers,作者Pete Callaway)

iOS 8 新加入一个类:UIPresentationController,它与 iOS 7 新添加的几个类与协议一道,帮助我们方便快捷地实现 ViewController 的自定义过渡效果。我发现要搞懂一个新的 API,最快的方法还是写一个例子。废话不多说,上例子!
该项目在GitHub可以下载。
 
实现自定义过渡
 
我们需要两个对象来实现自定义过渡,一个 UIPresentationController 的子类以及一个遵从 UIViewControllerAnimatedTransitioning 协议的类。
 
我们的 UIPresentationController 的子类是负责「被呈现」及「负责呈现」的 controller 以外的 controller 的,看着很绕口,说白了,在我们的例子中,它负责的仅仅是那个带渐变效果的黑色半透明背景 View。
 
而 UIViewControllerAnimatedTransitioning 类将会负责「被呈现」的 ViewController 的过渡动画。首先我们看 UIPresentationController。
 
UIPresentationController
 
在我们的 UIPresentationController 中,我们需要重写其中5个方法:
 
* presentationTransitionWillBegin
* presentationTransitionDidEnd:
* dismissalTransitionWillBegin
* dismissalTransitionDidEnd:
* frameOfPresentedViewInContainerView
 
presentationTransitionWillBegin 是在呈现过渡即将开始的时候被调用的。我们在这个方法中把半透明黑色背景 View 加入到 containerView 中,并且做一个 alpha 从0到1的渐变过渡动画。
  1. override func presentationTransitionWillBegin() { 
  2.     // 添加半透明背景 View 到我们的视图结构中 
  3.     self.dimmingView.frame = self.containerView.bounds 
  4.     self.dimminView.alpha = 0.0 
  5.  
  6.     self.containerView.addSubview(self.dimmingView) 
  7.     self.containerView.addSubview(self.presentedView()) 
  8.  
  9.     // 与过渡效果一起执行背景 View 的淡入效果 
  10.     let transitionCoordinator = self.presentingViewController.transitionCoordinator() 
  11.     transitionCoordinator.animateAlongsideTransition({(context: UIViewControllerTransitionCoordinatorContext!) -> Void in 
  12.         self.dimmingView.alpha  = 1.0 
  13.     }, completion:nil) 
通过使用「负责呈现」的 controller 的 UIViewControllerTransitionCoordinator,我们可以确保我们的动画与其他动画一道儿播放。
 
presentationTransitionDidEnd: 是在呈现过渡结束时被调用的,并且该方法提供一个布尔变量来判断过渡效果是否完成。在我们的例子中,我们可以使用它在过渡效果已结束但没有完成时移除半透明的黑色背景 View。
  1. override func presentationTransitionDidEnd(completed: Bool)  { 
  2.     // 如果呈现没有完成,那就移除背景 View 
  3.     if !completed { 
  4.         self.dimmingView.removeFromSuperview() 
  5.     } 
以上就涵盖了我们的背景 View 的呈现部分,我们现在需要给它添加淡出动画并且在它消失后移除它。正如你预料的那样,dismissalTransitionWillBegin 正是我们把它的 alpha 重新设回0的地方。
  1. override func dismissalTransitionWillBegin()  { 
  2.     // 与过渡效果一起执行背景 View 的淡出效果 
  3.     let transitionCoordinator = self.presentingViewController.transitionCoordinator() 
  4.     transitionCoordinator.animateAlongsideTransition({(context: UIViewControllerTransitionCoordinatorContext!) -> Void in 
  5.         self.dimmingView.alpha  = 0.0 
  6.     }, completion:nil) 
我们还需要在消失完成后移除背景 View。做法与上面 presentationTransitionDidEnd: 类似,我们重载 dismissalTransitionDidEnd: 方法
  1. override func dismissalTransitionDidEnd(completed: Bool) { 
  2.     // 如果消失没有完成,那么把背景 View 移除 
  3.     if completed { 
  4.         self.dimmingView.removeFromSuperview() 
  5.     } 
还有最后一个方法需要重载。在我们的自定义呈现中,被呈现的 view 并没有完全完全填充整个屏幕,而是很小的一个矩形。被呈现的 view 的过渡动画之后的最终位置,是由 UIPresentationViewController 来负责定义的。我们重载 frameOfPresentedViewInContainerView 方法来定义这个最终位置
  1. override func frameOfPresentedViewInContainerView() -> CGRect { 
  2.     // 我们可不希望被呈现的 View 占据了整个屏幕,所以我们调整他的frame 
  3.     var frame = self.containerView.bounds; 
  4.     frame = CGRectInset(frame, 50.0, 200.0) 
  5.  
  6.     return frame 
最终完整的类可以在此浏览。
 
UIViewControllerAnimatedTransitioning
 
我之前已经在上一篇博文中提到了 UIViewControllerAnimatedTransitioning 协议的使用。感谢 iOS 8 新加入的 UIPresentationController, 我们的 UIViewControllerAnimatedTransitioning 类可以比之前少做一些事了,现在,它只负责与呈现相关的 ViewController 的 View 的动画了,其他额外的 View 一概再管了,比如我们的黑色背景 View。
 
我们需要实现两个协议方法
 
* transitionDuration:
* animatedTransition:
 
我们还需要放一个类的 property,这样我们可以在类的初始化方法中定义它,用来区别我们到底是让 ViewController 呈现还是消失。
 
transitionDuration: 方法比较简单,我们要做的仅仅是返回一个动画持续时长。
 
在 animateTransition: 方法中,我们给被呈现的 view 添加呈现与消失的动画。
  1. func animateTransition(transitionContext: UIViewControllerContextTransitioning!)  { 
  2.     if isPresenting { 
  3.         animatePresentationWithTransitionContext(transitionContext) 
  4.     } 
  5.     else { 
  6.         animateDismissalWithTransitionContext(transitionContext) 
  7.     } 
  1. func animatePresentationWithTransitionContext(transitionContext: UIViewControllerContextTransitioning) { 
  2.     let presentedController = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey) 
  3.     let presentedControllerView = transitionContext.viewForKey(UITransitionContextToViewKey)! 
  4.     let containerView = transitionContext.containerView()! 
  5.  
  6.     // 设定被呈现的 view 一开始的位置,在屏幕下方 
  7.     presentedControllerView.frame = transitionContext.finalFrameForViewController(presentedController) 
  8.     presentedControllerView.frame.origin.y = containerView.bounds.size.height 
  9.  
  10.     containerView.addSubview(presentedControllerView) 
  11.  
  12.     // 添加一个动画,让被呈现的 view 移动到最终位置,我们使用0.6的damping值让动画有一种duang-duang的感觉…… 
  13.     UIView.animateWithDuration(transitionDuration(transitionContext), delay: 0.0, usingSpringWithDamping: 0.6, initialSpringVelocity: 0.0, options: .AllowUserInteraction, animations: { 
  14.         presentedControllerView.center.y = containerView.bounds.size.height/2 
  15.     }, completion: {(completed: Bool) -> Void in 
  16.         transitionContext.completeTransition(completed) 
  17.     }) 
  18.  
  19. func animateDismissalWithTransitionContext(transitionContext: UIViewControllerContextTransitioning) { 
  20.     let presentedControllerView = transitionContext.viewForKey(UITransitionContextFromViewKey)! 
  21.     let containerView = transitionContext.containerView()! 
  22.  
  23.     // 添加一个动画,让要消失的 view 向下移动,离开屏幕 
  24.     UIView.animateWithDuration(transitionDuration(transitionContext), delay: 0.0, usingSpringWithDamping: 1.0, initialSpringVelocity: 0.0, options: .AllowUserInteraction, animations: { 
  25.         presentedControllerView.frame.origin.y = containerView.bounds.size.height 
  26.     }, completion: {(completed: Bool) -> Void in 
  27.             transitionContext.completeTransition(completed) 
  28.     }) 
完整的类可以在此浏览。
 
使用自定义呈现类
 
我们已经实现了我们所需的实现自定义呈现的类。接下来我们看看怎么使用它们。事实上,有许多种不同的方法来使用它们,不过最简单的方法还是让被呈现的 ViewController 来作为自己的 UIViewControllerTransitioningDelegage
  1. init(coder aDecoder: NSCoder!) { 
  2.     super.init(coder: aDecoder) 
  3.     self.commonInit() 
  4.  
  5. init(nibName nibNameOrNil: String!, bundle nibBundleOrNil: NSBundle!)  { 
  6.     super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) 
  7.     self.commonInit() 
  8.  
  9. func commonInit() { 
  10.     self.modalPresentationStyle = .Custom 
  11.     //让被呈现 viewController 自己来作为自己的 transitioningDelegate 
  12.     self.transitioningDelegate = self 
现在我们的 ViewController 就可以在呈现自己时,提供给我们一个 UIPresentationController 的实例了。
  1. func presentationControllerForPresentedViewController(presented: UIViewController!, presentingViewController presenting: UIViewController!, sourceViewController source: UIViewController!) -> UIPresentationController! { 
  2.     if presented == self { 
  3.         return CustomPresentationController(presentingViewController: presenting, presentedViewController: presented) 
  4.     } 
  5.     else { 
  6.         return nil 
  7.     } 
当然,还要有一个遵从了 UIViewControllerAnimatedTransitioning 协议的类的实例来负责动画
  1. func animationControllerForPresentedController(presented: UIViewController!, presentingController presenting: UIViewController!, sourceController source: UIViewController!) -> UIViewControllerAnimatedTransitioning! { 
  2.     if presented == self { 
  3.         return CustomPresentationAnimationController(isPresenting: true
  4.     } 
  5.     else { 
  6.         return nil 
  7.     } 
  8.  
  9. func animationControllerForDismissedController(dismissed: UIViewController!) -> UIViewControllerAnimatedTransitioning! { 
  10.     if dismissed == self { 
  11.         return CustomPresentationAnimationController(isPresenting: false
  12.     } 
  13.     else { 
  14.         return nil 
  15.     } 
完整的类可以在此浏览。
 
尾声
 
就像我上篇博文中说的那样,希望这篇文章能帮你创建自己的自定义呈现效果。文中例子的工程文件已上传至 GitHub

CocoaChina是全球最大的苹果开发中文社区,官方微信每日定时推送各种精彩的研发教程资源和工具,介绍app推广营销经验,最新企业招聘和外包信息,以及Cocos2d引擎、Cocos Studio开发工具包的最新动态及培训信息。关注微信可以第一时间了解最新产品和服务动态,微信在手,天下我有!

请搜索微信号“CocoaChina”关注我们!

搜索CocoaChina微信公众号:CocoaChina

顶部