利用转场动画实现slide down menu

这个是今天我们要完成的东东

项目模板

你可以从这里下载最初的项目模板下载项目模板。现在的这个项目中包括storyboard和视图控制器类,一个是main screen,另一个是导航菜单。当你下载并运行程序的时候,你会看见一个有模型数据的主界面。

在开始之前,请花一些时间来熟悉一下这个工程。

main storyboard界面

现在开始实现

当你打开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:选项,如下图 联线Exit直到现在,当用户点击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

到这里,我们就完成全部的代码了,

全部文件代码

转载于:https://my.oschina.net/zboy/blog/489540

原文链接:加载失败,请重新获取