资讯专栏INFORMATION COLUMN

使用视图控制器的切换创建下拉菜单栏

hover_lew / 3216人阅读

摘要:下拉菜单则是另一种常用的菜单设计。为了退出下拉菜单视图,我们添加一个。我们将会在类中使用这两者来实现动画转场,但是在此之前我们我们先来看看下拉菜单是怎么工作的。创建下拉菜单动画为了实现下拉菜单的动画效果,我们会创建一个名为动画管理器。

作者:Simon NG,时间:2016/7/29
翻译:BNCoding, 如有错误欢迎指出。原文链接

导航是用户交互的一个重要组成部分,有很多种方式提供一个菜单栏让用户来自由切换想访问的功能。在之前的教程中我们介绍了其中的一种就是侧滑栏。下拉菜单则是另一种常用的菜单设计。当用户点击菜单按键的是时候主屏幕中下拉显示出菜单选项。如果你不知道下拉菜单是如何实现的话,不需要忧虑。继续阅读文章马上你就能看见一个演示动画。

在展示下拉菜单的实现之前,这篇文章已经假设你对自定义视图切换有一定的了解。如果你对这个视图切换有关的内容不太熟悉的话,那么你可以先去看下Joyce写的这篇文章

好了现在进入正题。

下拉菜单功能的开始演示

在这篇教程中我们会用Swift语言来实现下拉菜单,下面就是最终效果的一个快速展示:

工程模版

和以往一样,我不希望你从头开始,你可以先去下载起始工程。该起始工程里面包含了storyboard和一些view controller的类。你可以找到两个tableview,其中一个用于主屏幕(嵌在导航控制器中),另一个用于导航菜单。如果你运行程序的话,主页会展示一些虚拟的数据。

再继续下一步之前,我们先花点时间去浏览一些工程熟悉一下代码。

模态展示菜单视图

首先,我们打开Main.storyboard文件,找到里面的两个tableview,因为两者还没有进行segue链接。为了让用户点击menu按键的时候能够展示出菜单,我们按住control键点击menu按键拖动到菜单的tableview。松开按键,在action segue里面选择”present modally“。

如果你现在运行app的话,菜单界面会以模态的形式展现出来。为了退出下拉菜单视图,我们添加一个unwind segue

打开NewsTableViewController.swift文件,然后添加一个unwind action方法:

@IBAction func unwindToHome(segue: UIStoryboardSegue) {
    let sourceController = segue.sourceViewController as! MenuTableViewController
    self.title = sourceController.currentItem
}

接下来,我们回到storyboard,按住control键链接Menutableview中的prototype cellexit图标,在selection segue选项下面选择unwindToHome

如果用户现在点击任意一个菜单的话,那么当前视图会消失而主视图将会呈现出来。通过unwindToHome函数,主视图控制器(即NewsTableViewController)会根据用户选择的菜单相应的改变其标题。为了简单其间我们除了标题我们不会修改主视图中的内容。

除此之外,我们还将设置当前的选中项为白色。在实现这些希望的结果之前我们还要实现一系列的函数方法。

将下面的函数插入到MenuTableViewController类里面:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    let menuTableViewController = segue.sourceViewController as! MenuTableViewController
    if let selectedRow = menuTableViewController.tableView.indexPathForSelectedRow()?.row {
        currentItem = menuItems[selectedRow]
    }
}

在新版本的Swift中上面的代码编译无法通过,后面我会将附上自己修改后的完整代码

这个函数里面我们只是正确设置了当前选择的菜单。

NewsTableViewController.swift文件里面插入下面这个函数,该函数会将当前的标题传递给menu controller。

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    let menuTableViewController = segue.destinationViewController as! MenuTableViewController
    menuTableViewController.currentItem = self.title!
}

现在编译并运行程序的话,当你点击菜单按键的时候菜单选择视图会模态的出现,当你选择其中的一个菜单项的时候程序会回到主视图并且相应的修改主视图标题。

创建带动画过渡的下了菜单

当前的菜单视图示使用系统标准的动画进行转场的,现在我们需要创建一个自定义的转场动画。正如我在之前的文章中介绍的那样,自定义视图控制器的转场动画的核心是动画对象,该对象同时遵守UIViewControllerAnimatedTransitioningUIViewControllerTransitioningDelegate协议。我们将会在类中使用这两者来实现动画转场,但是在此之前我们我们先来看看下拉菜单是怎么工作的。当用户点击菜单按键的时候,主视图会慢慢的往下面移动直到它到达指定的位置,也就是视图底部在往下180个单位。

创建下拉菜单动画

为了实现下拉菜单的动画效果,我们会创建一个名为MenuTransitionManager动画管理器。在工程导航中右击创建一个新文件。文件中的创建一个NSObject的子类MenuTransitionManager

类的代码如下:

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
    }
 
}

该类同时遵循了UIViewControllerAnimatedTransitioningUIViewControllerTransitioningDelegate协议。因为以前的文章中已经介绍了,这里我就不会讲解其中的细节了。我们仔细查看其中的animation block(例如animateTransition方法)。

参考之前显示过程,在整个转场动画其间main viewfromViewmenu viewtoView

为了实现我们要的效果,我们配置了两个转场动画。第一个是向下移动main view,另一个则是上移menu view这样当该视图回到原先的位置的时候可以再次响应事件并进行动画转场。稍后运行程序的时候就明白我的意思了。

从iOS7开始,你可以通过UIView-Snapshotting API快速、简单的创建一个轻量级的视图快照。

snapshot = fromView.snapshotViewAfterScreenUpdates(true)

通过调用snapshotViewAfterScreenUpdates函数,你就获得的main view的快照了。有了快照之后,我们就可以在动画转场的容器里添加该快照了。该快照在menu view之后添加以保证该快照在容器的最顶部。

菜单视图的出场动画的实现其实很简单。我们仅仅在主视图的snapshot上面使用moveDown转场将菜单栏视图设置为默认位置。

self.snapshot?.transform = moveDown
toView.transform = CGAffineTransformIdentity

当菜单栏消失的时候采取的动作是相反的。主视图向上滑动并且恢复到默认的位置。另外我们会将快照移出这样真正的主视图就会展现出来。

现在我们打开NewsTableViewController.swift文件并声明一个MenuTransitionManager对象:

var menuTransitionManager = MenuTransitionManager()

prepareForSegue方法里面添加一行代码设置好该动画委托对象。

现在编译运行代码,点击菜单按键你就能看见一个下拉的菜单栏视图了。

检查用户的手势

到现在为止我们只能通过选择某一个菜单选项来使菜单栏视图消失。从用户的角度来考虑的话,其实点击主视图的快照该菜单栏视图也应该消失。但是主视图的快照是没有进行响应的。

事实上snapshot也是一个UIView对象,所以我们可以创建一个UITapGestureRecognizer对象并且将其添加到snapshot上。

当我们对UITapGestureRecognizer对象进行初始化的时候,我们需要传递目标对象和需要被调用的函数名。很明显你可以硬编码一个特定的对象来让菜单栏视图消失,但是为了我们的设计更加的灵活我们声明一个协议对象,并且让委托对象实现该协议中的方法。

MenuTransitionManager.swift里面,我们如下声明该协议:

@objc protocol MenuTransitionManagerDelegate {
    func dismiss()
}

在这里我们定义了一个MenuTransitionManagerDelegate协议,该协议里面还有一个必须要实现的方法。委托对象必须实现dismiss()方法来完成逻辑上的视图消失操作。

MenuTransitionManager类里面,声明一个委托变量:

var delegate:MenuTransitionManagerDelegate?

然后需要处理该点击事件的对象将会被设置为委托对象。

最后,我们需要创建一个UITapGestureRecognizer对象并且添加多snapshot中。一个好的解决方法就是在snapshot变量中使用didset方法。我们将snapshot定义声明进行修改:

var snapshot:UIView? {
    didSet {
        if let _delegate = delegate {
            let tapGestureRecognizer = UITapGestureRecognizer(target: _delegate, action: "dismiss")
            snapshot?.addGestureRecognizer(tapGestureRecognizer)
        }
    }
}

属性观察器(Property observer)是Swift中一个非常强大的特性。在设置属性的值的时候观察器(willSet/didSet)都将会被调用。该特性让我们在变量赋值的前后都可以很方便的立刻采取一些特定的操作。willSet方法会在属性值赋值设置之前立刻被调用,而didSet方法会在属性值赋值设置完成后立刻被调用。

在上面的代码中,我们在属性观察器方法中创建了一个UITapGestureRecognizer对象并且将它添加给了snapshot。所以,每次我们对snapshot变量进行赋值之后都会立马配置一个UITapGestureRecognizer对象。

大部分的工作都差不多完成了。现在我们回到NewsTableViewController.swift中设置该类遵循MenuTransitionManagerDelegate协议并且实现里面的方法。

首先,我们如下修改相关的声明:

class NewsTableViewController: UITableViewController, MenuTransitionManagerDelegate

接下来我们实现其中的方法:

func dismiss() {
    dismissViewControllerAnimated(true, completion: nil)
}

在上面的代码中,我们使用dismissViewControllerAnimated方法来将退出当前的视图。

最后,我们在NewsTableViewController类的prepareForSegue方法中添加一行代码来设置委托对象:

self.menuTransitionManager.delegate = self

任务完成,现在你编译运行程序并点击主视图的snapshot的话,菜单栏视图将会消失。

通过自定义的视图转换动画,你可以大大的提高用户的体验让你的应用脱颖而出。下拉菜单栏紧急是一个示例而已,在你自己的下一个应用中你可以做出自己的动画实现。

为了读者进行参照,你可以下载完整的代码

因为教程的编写的时间较早以及Swift版本的更迭,文章的部分代码已经不在适用或者部分代码有更好的方法来实现。附上我的代码。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/16232.html

相关文章

  • GitHub 上受欢迎 Android UI Library 整理二

    摘要:通知克服和的限制高仿淘宝微信等热门通知视图通过一行代码实现通知功能工具类轮播框通知全新的通知栏聊天视图简化开发快速创建聊天信息视图聊天视图实现聊天界面基于实际需求做出的灵活可定制的功能模仿饿了么详情页的例子带 通知 https://github.com/Tapadoo/Al... ★2528 - 克服Toast和Snackbar的限制https://github.com/wenming...

    nifhlheimr 评论0 收藏0
  • 嵌套滚动效果实现讨论

    摘要:之所以在前面给出了四个例子,是因为淘票票和简书采用的是上面提到的方案,而抖音和即刻两个则不是,并且即刻在体验上更完美,这个后面会讲到。 本文要讨论的是类似于即刻、淘票票首页,抖音、简书个人主页这样的嵌套滚动效果,事实上网上已经有很多的相关的文章,比如: 嵌套UIScrollview的滑动冲突解决方案 iOS 嵌套UIScrollview的滑动冲突另一种解决方案 多层 UIScroll...

    Joyven 评论0 收藏0
  • GitHub 上排名前 100 Objective-C 项目简介

    摘要:主要对当前排名前的项目做一个简单的简介方便初学者快速了解到当前在的情况地址若有任何疑问可通过微博李锦发联系我项目名称项目信息作者是的博主开发界的大神级人物毕业于卡内基梅隆大学开源了许多牛逼的项目这个便是其中之一采用主要方便与服务端进 主要对当前 GitHub 排名前 100 的项目做一个简单的简介, 方便初学者快速了解到当前 Objective-C 在 GitHub 的情况. Git...

    stdying 评论0 收藏0
  • Kibana 用户指南(使用Flight仪表盘探索Kibana)

    摘要:使用仪表盘探索你是的新手并希望尝试一下,只需单击一下,你就可以安装样本数据并开始与交互。例如,此仪表盘显示从伦敦到和的航班数据。单击选项卡以查看请求统计信息,请求和中的响应。 使用Flight仪表盘探索Kibana 你是Kibana的新手并希望尝试一下,只需单击一下,你就可以安装Flights样本数据并开始与Kibana交互。 Flight数据集包含四家航空公司的数据,你可以从Kiba...

    stackfing 评论0 收藏0
  • Coder Essential之客户端知识索引(iOS/Android/Web)

    摘要:本文主要面向笔者在等移动端开发中的经验总结出在现有以及未来的所有客户端的学习中应该掌握的知识脉络图。它表示的是一种常见的客户端软件开发框架。部分放在这边是因为它是一套从开始到后面响应中的完整的机制,以中的中的以及中的为典型代表。 [TOC] 本文主要面向笔者在Web、iOS、Android、WP等移动端开发中的经验总结出在现有以及未来的所有客户端的学习中应该掌握的知识脉络图。通俗来说,...

    AlienZHOU 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<