接着可交互滑动侧边栏(上)的内容,今次我们将在之前的基础上,做出个完整可用的侧边栏。Previously on我的教程,我们通过自定义View Controller Transition以及快照的巧妙使用,成功实现了打开和关闭菜单的动画(如下图所示),但并没有添加任何交互效果。如果能通过左右滑动的方式打开/关闭侧边栏,想必是极好的。
打开上次的半成品,我们接着开码。
添加Dismiss动作
我们通过滑动手势识别器来驱动过渡动画,当用户水平滑动屏幕时,关闭动画会随你手势而动。
打开MenuViewController.swift并添加下面的代码:
importUIKit
classMenuViewController: UIViewController{
// 1
varinteractor:Interactor?=nil
// 2
@IBAction funchandleGesture(sender:UIPanGestureRecognizer){
// 3
lettranslation=sender.translationInView(view)
// 4
letprogress=MenuHelper.calculateProgress(
translation,
viewBounds:view.bounds,
direction:.Left
// 5
MenuHelper.mapGestureStateToInteractor(
sender.state,
progress:progress,
interactor:interactor){
// 6
self.dismissViewControllerAnimated(true,completion:nil)
@IBAction funccloseMenu(sender:AnyObject){
dismissViewControllerAnimated(true,completion:nil)
解释一下上面的代码:
- 注释1:MainViewController把interactor对象传递给了MenuViewController,用于同步状态机的状态
- 注释2:给滑动手势创建对应的@IBAction方法,随后我们会将它与Storyboard中的手势相连
- 注释3:通过
translationView()方法获得手势当前坐标 - 注释4:调用MenuHelper的
calculateProgress()方法,把手势坐标转化为特定方向(这里是.Left)上的滑动进度(滑动距离/屏幕距离) - 注释5:把所需信息传入MenuHelper的
mapGestureStateToInteractor()方法,进行手势状态和过渡动画之间的同步 - 注释6:注意这里的闭包并不是Completion Handler(方法完成后调用的闭包)。我们需要把启动动画的代码放在这里,即
dismissViewControllerAnimated()
每一次触发滑动手势都会调用上面的代码,更新过渡动画的进度。假如上面的代码弄懵了你,不要悲伤,不要心急!忧郁的日子里需要回顾上一篇教程中的添加辅助文件,相信吧,看懂代码的日子终会来临!
添加滑动手势识别器
还记得我们刚刚创建的@IBAction吗?是时候把它和Storyboard相连接了。
- 从Object Library拖出一只手势识别器,扔到之前创建的Close按钮上
- 按住Control不放,把手势识别器图标拖到Menu View Controller的Scene图标上
- 在弹出菜单中选中
handleGesture:
多亏一位叫做Aly的读者告诉我,把手势识别器放到Close按钮上的效果要比放到背景视图上好。这会让快照更像一个真实存在的对象(好悲伤的一句话,默哀一秒钟)。此外,如果你准备在菜单栏里使用Table View并给Cell添加滑动手势的话(好反人类的设计),这种方法还可以避免手势冲突。
连接Dismiss动作
在手势起作用之前还差一步:我们需要告诉MainViewController,关闭菜单时过渡动画的交互被我们承包了。
把MainViewController.swift的内容替换成下面的代码:
importUIKit
classMainViewController: UIViewController{
// 1
letinteractor=Interactor()
@IBAction funcopenMenu(sender:AnyObject){
performSegueWithIdentifier("openMenu",sender:nil)
overridefuncprepareForSegue(segue:UIStoryboardSegue,sender:AnyObject?){
ifletdestinationViewController=segue.destinationViewControlleras?MenuViewController{
destinationViewController.transitioningDelegate=self
// 2
destinationViewController.interactor=interactor
extensionMainViewController: UIViewControllerTransitioningDelegate{
funcanimationControllerForPresentedController(presented:UIViewController,presentingController presenting:UIViewController,sourceController source:UIViewController)->UIViewControllerAnimatedTransitioning?{
returnPresentMenuAnimator()
funcanimationControllerForDismissedController(dismissed:UIViewController)->UIViewControllerAnimatedTransitioning?{
returnDismissMenuAnimator()
// 3
funcinteractionControllerForDismissal(animator:UIViewControllerAnimatedTransitioning)->UIViewControllerInteractiveTransitioning?{
returninteractor.hasStarted?interactor:nil
老规矩,解释一下其中几处:
- 注释1:还记得我们传来传去的interactor对象吗?这里就是最初创建它的地方
- 注释2:把interactor传给
prepareForSegue()方法 - 注释3:只有当用户滑动时(之前的代码中,我们在手势开始时把
hasStarted设置为了true,这里正好用上)才会返回interactor,表示动画将以交互的形式进行
目前为止的代码结构:
- MainViewController.swift
- MenuViewController.swift
- Interactor.swift(未修改)
- MenuHelper.swift(未修改)
- PresentMenuAnimator.swift(未修改)
- DismissMenuAnimator.swift(未修改)
编译运行,现在应该能通过拖拽快照关闭菜单了。
添加Present动作
就快完成了!其实我们可以就此打住,这已经是一个非常实用的滑动侧边栏了。但如果打开菜单也能通过手势交互完成多好?
译者:Apple不提倡滑动侧边栏的原因正在于此。原生软件里的手势都严格遵循统一的设计哲学,从左边缘向右滑动代表着“返回”,无论是Safari里的返回上一页面,还是无处不在的返回上级内容,这与侧边栏的设计相悖。其实不单是Apple,就连重度使用侧边栏的Google,为了保证手势意义的统一,也没有添加滑动展开菜单的手势。但另一方面,如果添加了侧边栏却不添加手势,那么用户就不得不去按左上角的菜单图标,没有个13厘米长的大拇指根本无法正常玩耍。
个人原则是,不常用的设置性内容可以放到侧边栏里,尽量不使用滑动展开的手势;否则用Tab Bar Controller代替,避免过度设计。
这里我们不使用普通的手势识别器,而是用Screen Edge Pan Gesture Recognizer(边缘滑动手势识别器),以此把它对主内容区手势的影响降到最低。
和其他手势一样,你需要引导用户,让他们知道你的App具有这样的手势特性。好在对重度依赖滑动手势的用户来说,这样的手势并不陌生。如果想要了解如何制作手势引导动画,可以参考这里。
打开MainViewController.swift并添加下面的代码:
@IBAction funcedgePanGesture(sender:UIScreenEdgePanGestureRecognizer){
lettranslation=sender.translationInView(view)
letprogress=MenuHelper.calculateProgress(translation,viewBounds:view.bounds,direction:.Right)
MenuHelper.mapGestureStateToInteractor(
sender.state,
progress:progress,
interactor:interactor){
self.performSegueWithIdentifier("openMenu",sender:nil)
和之前我们在MenuViewController里添加的@IBAction方法非常相似。edgePanGesture调用了MenuHelper的方法,负责手势状态和过渡动画间的同步。
添加边缘滑动手势识别器
- 从Object Library里拽一个边缘手势识别器并拖到MainViewController上(蓝色)
- 按住Control不放,把手势图标拽到MainViewController的Scene图标上
- 在弹出菜单中选择
edgePanGesture:动作 - 在手势的Attributes Inspector里勾选Left
操作和之前差不多,唯一的区别是,这次需要设定手势仅使用屏幕左边缘。
连接Present动作
最后的最后,我们同样需要告诉过渡动画代理,我们将接管打开菜单时的所有交互。
打开MainViewController.swift,在UIViewControllerAnimatedTransitioning扩展里添加下面的代码:
funcinteractionControllerForPresentation(animator:UIViewControllerAnimatedTransitioning)->UIViewControllerInteractiveTransitioning?{
returninteractor.hasStarted?interactor:nil
完成收工!
最后检查一下代码结构:
- MainViewController.swift
- MenuViewController.swift(未修改)
- Interactor.swift(未修改)
- MenuHelper.swift(未修改)
- PresentMenuAnimator.swift(未修改)
- DismissMenuAnimator.swift(未修改)
编译运行,应该能通过滑动手势打开菜单了。
总结
你可以在这里查看完成的项目代码。此外,在示例项目的菜单视图里,我们还添加里一些Table View Cell,用于打开相应详细视图。
私以为,自定义View Controller Transition不失为一种优雅创建交互侧边栏的方法。通过快照的巧妙使用,营造出了屏幕上同时存在两个View Controller的错觉。除此之外,我们还可以自由定义过渡动画效果,满足各种变态需求。
原文链接:https://www.thorntech.com/2016/03/ios-tutorial-make-interactive-slide-menu-swift/
第一时间获取iOS优质中文教程,扫描下方的二维码订阅程序猴猴猴,或订阅我的博客:www.jiarui-blog.com。
交互式滑动侧边栏(下)





