项目模板
你可以从这里下载最初的项目模板下载项目模板。现在的这个项目中包括storyboard和视图控制器类,一个是main screen,另一个是导航菜单。当你下载并运行程序的时候,你会看见一个有模型数据的主界面。
在开始之前,请花一些时间来熟悉一下这个工程。
现在开始实现
当你打开Main.stroyboard文件的时候,你会看到两个tableView Controllers,当然,现在还没有看到任何利用segue实现的链接。为了实现我们想要的效果,参照下图实现动作,选择"present modally"这个动作segue 如果,现在运行这个工程,这个菜单将会modal出来,为了能够dismiss这个菜单,我们需要添加一个unwind segue.
打开NewsTableViewController.swift文件,然后插入unwind动作方法
@IBAction func unwindToHome(segue: UIStoryboardSegue) { let sourceController = segue.sourceViewController as! MenuTableViewController self.title = sourceController.currentItem }
下一步,到storyboard中,找到Menu table view controller 中的prototype cell然后联线到控制器右上角的exit。然后在segue选项中选择unwidToHome:选项,如下图 直到现在,当用户点击Menu item,这个菜单控制器就会dismiss出main screen。通过unwindToHome:这个方法,用户可以在选择menu item和改变标题的时候,回到主菜单。但是,为了保持简单,我们不想通过改变navigation bar的标题来世现弹出main screen 的内容。
另外,我们来一起实现,在选择item的时候实现高亮,那么我们就要实现下面的方法。 在MenuTableViewController类中插入下面的方法
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { let menuTableViewController = segue.sourceViewController as! MenuTableViewController if let selectedRow = menuTableViewController.tableView.indexPathForSelectedRow()?.row { currentItem = menuItems[selectedRow] } }
到这里,我们只是设置了正在选择的menu item。在NewsTableViewController.swift中添加下面的代码,
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { let menuTableViewController = segue.destinationViewController as! MenuTableViewController menuTableViewController.currentItem = self.title! }
现在运行代码,点击menu item,你可以看到弹出菜单控制器,当你选择其中的选项,你可以看到dismiss菜单,并且会出现一个新的标题和新的控制器。如下图:
创建Slide Down menu动画
动画的基本形式可以参考下面图片的样式 e
开始创建动画
在xcode 中新建一个类,名称叫做MenuTransitionManager继承自NSObject.
添加如下代码:
class MenuTransitionManager: NSObject,UIViewControllerAnimatedTransitioning,UIViewControllerTransitioningDelegate { var duration = 0.5 var isPresenting = false var snapshot:UIView?
func transitionDuration(transitionContext: UIViewControllerContextTransitioning) -> NSTimeInterval { return duration } func animateTransition(transitionContext: UIViewControllerContextTransitioning) { // Get reference to our fromView, toView and the container view let fromView = transitionContext.viewForKey(UITransitionContextFromViewKey)! let toView = transitionContext.viewForKey(UITransitionContextToViewKey)! // Set up the transform for sliding let container = transitionContext.containerView() let moveDown = CGAffineTransformMakeTranslation(0, container.frame.height - 150) let moveUp = CGAffineTransformMakeTranslation(0, -50) // Add both views to the container view if isPresenting { toView.transform = moveUp snapshot = fromView.snapshotViewAfterScreenUpdates(true) container.addSubview(toView) container.addSubview(snapshot!) } // Perform the animation UIView.animateWithDuration(duration, delay: 0.0, usingSpringWithDamping: 0.9, initialSpringVelocity: 0.3, options: nil, animations: { if self.isPresenting { self.snapshot?.transform = moveDown toView.transform = CGAffineTransformIdentity } else { self.snapshot?.transform = CGAffineTransformIdentity fromView.transform = moveUp
} },
completion: { finished in transitionContext.completeTransition(true) if !self.isPresenting { self.snapshot?.removeFromSuperview() } }) } ``` ``` func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
isPresenting = false return self } ``` func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? { isPresenting = true return self } }
看到上面的代码,让我们专注于animation block (i.e. animateTransition 方法)这里主要包括主视图的from view 和to View。 创建这个动画,我们需要实现两个转场,前一个是用来实现向下移动菜单,另一个是实现向上移动菜单。你通过运行程序,以及接下来的介绍后明白我的意思的。 在iOS7以及之后的系统,你都可以使用UIView-Snapshotting API来快速并且简单的创建一个轻量级的View快照。
snapshot = fromView.snapshotViewAfterScreenUpdates(true)
实际的弹出菜单动画其实很简单,使用下面的代码就可以了
self.snapshot?.transform = moveDown toView.transform = CGAffineTransformIdentity
当我们需要dismiss menu我们就需要做和上面相反的事情。 那么接下来,打开NewsTableViewController.swift 然后为MenuTransitionManager object声明一个变量
var menuTransitionManager = MenuTransitionManager()
在prepareForSegue方法中,添加下面的代码来实现和animation挂钩
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { let menuTableViewController = segue.destinationViewController as! MenuTableViewController menuTableViewController.currentItem = self.title! menuTableViewController.transitioningDelegate = self.menuTransitionManager }
直到这里,你基本完成了这个项目。
点击手势
中间的都是一段废话,我们需要重点,那就是代码 在MenuTransitionmanager.swift中,定义一个协议
@objc protocol MenuTransitionManagerDelegate { func dismiss () }
接下来,就我们需要去创建UITapGestureRecognizer object以及添加snapshot。在snapshot变量中我们声明didSet方法,修改方法如下
var snapshot:UIView? { didSet { if let _delegate = delegate { let tapGestureRecognizer = UITapGestureRecognizer(target:_target, action:"dismiss") snapshot?.addGestureRecognizer(tapGestureRecognizer) } } }
属性观察是swift众多强悍的使用方法之一。观察(willSet/didSet)一个属性被设置的时间。这为我们提供了一个方便的方式立即之前或转让后,执行某些操作。该willSet方法被称为值存储权利之前,而didSet方法分配后,立即调用。
在上面的代码中,我们使用酒店的观察者来创建一个手势识别并将其设置为快照。所以我们每次分配快照变量中的对象的时候,它会立即用点击手势识别配置。
我们几乎完成。现在回到NewsTableViewController.swift ,来实现MenuTransitionManagerDelegate协议的类。
首先,更改类声明如下:
class NewsTableViewController: UITableViewController, MenuTransitionManagerDelegate
接下来实现:
func dismiss() { dismissViewControllerAnimated(true, completion: nil) }
这样,我们完成了一个叫做dismissViewControllerAnimated的方法来dismissView Controller。下面我们需要在prepareForSegue方法中添加NewsTableViewCotnroller类,设置代理
self.menuTransitionManager.delegate = self
到这里,我们就完成全部的代码了,