Swift 侧滑菜单的主页向右滑动,露出下方菜单页实现例子

侧滑菜单是现在的APP上很常见的功能,其效果是在主界面用手指向右滑动,就可以将菜单展示出来,而主界面会被隐藏大部分,但是仍有左侧的一小部分同菜单一起展示。

虽然网上也有很多实现这种slide view效果的第三方库,但如果想自己写代码实现也是很简单的,效果图如下:

      

1,程序页面结构

   MainViewController:主页视图
   MenuViewController:菜单视图,当主视图侧滑后显示
   ViewController:页面容器视图,将上面两个视图加入到这里面

2,StoryBoard配置

在StoryBoard中添加两个新的View Controller(Storyoard ID分别是mainView、menuView),同时分别绑定MainViewController和MenuViewController这两个类。

3,代码讲解

(1)页面初始化完毕后我们会先把主页视图(MainViewController)添加进来,同时对其设置个拖动手势(UIPanGestureRecognizer)。
(2)当手指在屏幕从左向右滑动时,创建菜单视图(MenuViewController)并添加到页面最底部。同时主页视图会随着手指的移动做线性移动。
(3)手指离开后,根据主页视图的位置(是否滑动超过一半),程序自动将主页视图完全展开或收起。
(4)菜单完全展开时,手指点击主页突出的部分也会自动收起菜单。
(5)menuViewExpandedOffset属性是设置菜单展示出来后,主页面在左侧露出部分的宽度。
(6)currentState属性保存菜单的状态,同时监听它的didSet事件来设置主页面阴影(当菜单显示出来的时候,主页边框会添加阴影,这样有层次感,效果更好些。)

4,ViewController.swift代码如下

import UIKit
 
class ViewController: UIViewController {
    // 主页面控制器
    var mainViewController:MainViewController!
    
    // 菜单页控制器
    var menuViewController:MenuViewController?
    
    // 菜单页当前状态
    var currentState = MenuState.Collapsed {
        didSet {
            //菜单展开的时候,给主页面边缘添加阴影
            let shouldShowShadow = currentState != .Collapsed
            showShadowForMainViewController(shouldShowShadow)
        }
    }
    
    // 菜单打开后主页在屏幕右侧露出部分的宽度
    let menuViewExpandedOffset: CGFloat = 60
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        //添加主页面
        mainViewController = UIStoryboard(name: "Main", bundle: nil)
            .instantiateViewControllerWithIdentifier("mainView") as! MainViewController
        view.addSubview(mainViewController.view)
        
        //建立父子关系
        addChildViewController(mainViewController)
        mainViewController.didMoveToParentViewController(self)
        
        //添加拖动手势
        let panGestureRecognizer = UIPanGestureRecognizer(target: self,
            action: "handlePanGesture:")
        mainViewController.view.addGestureRecognizer(panGestureRecognizer)
        
        //单击收起菜单手势
        let tapGestureRecognizer = UITapGestureRecognizer(target: self,
            action: "handlePanGesture")
        mainViewController.view.addGestureRecognizer(tapGestureRecognizer)
    }
    
    //拖动手势响应
    func handlePanGesture(recognizer: UIPanGestureRecognizer) {
        
        switch(recognizer.state) {
        // 刚刚开始滑动
        case .Began:
            // 判断拖动方向
            let dragFromLeftToRight = (recognizer.velocityInView(view).x > 0)
            // 如果刚刚开始滑动的时候还处于主页面,从左向右滑动加入侧面菜单
            if (currentState == .Collapsed && dragFromLeftToRight) {
                currentState = .Expanding
                addMenuViewController()
            }
            
        // 如果是正在滑动,则偏移主视图的坐标实现跟随手指位置移动
        case .Changed:
            let positionX = recognizer.view!.frame.origin.x +
                recognizer.translationInView(view).x
            //页面滑到最左侧的话就不许要继续往左移动
            recognizer.view!.frame.origin.x = positionX < 0 ? 0 : positionX
            recognizer.setTranslation(CGPointZero, inView: view)
            
        // 如果滑动结束
        case .Ended:
            //根据页面滑动是否过半,判断后面是自动展开还是收缩
            let hasMovedhanHalfway = recognizer.view!.center.x > view.bounds.size.width
            animateMainView(hasMovedhanHalfway)
        default:
            break
        }
    }
    
    //单击手势响应
    func handlePanGesture() {
        //如果菜单是展开的点击主页部分则会收起
        if currentState == .Expanded {
            animateMainView(false)
        }
    }
    
    // 添加菜单页
    func addMenuViewController() {
        if (menuViewController == nil) {
            menuViewController = UIStoryboard(name: "Main", bundle: nil)
                .instantiateViewControllerWithIdentifier("menuView") as? MenuViewController
            
            // 插入当前视图并置顶
            view.insertSubview(menuViewController!.view, atIndex: 0)
            
            // 建立父子关系
            addChildViewController(menuViewController!)
            menuViewController!.didMoveToParentViewController(self)
        }
    }
    
    //主页自动展开、收起动画
    func animateMainView(shouldExpand: Bool) {
        // 如果是用来展开
        if (shouldExpand) {
            // 更新当前状态
            currentState = .Expanded
            // 动画
            animateMainViewXPosition(CGRectGetWidth(mainViewController.view.frame) -
                menuViewExpandedOffset)
        }
        // 如果是用于隐藏
        else {
            // 动画
            animateMainViewXPosition(0) { finished in
                // 动画结束之后s更新状态
                self.currentState = .Collapsed
                // 移除左侧视图
                self.menuViewController?.view.removeFromSuperview()
                // 释放内存
                self.menuViewController = nil;
            }
        }
    }
    
    //主页移动动画(在x轴移动)
    func animateMainViewXPosition(targetPosition: CGFloat,
        completion: ((Bool) -> Void)! = nil) {
        //usingSpringWithDamping:1.0表示没有弹簧震动动画
        UIView.animateWithDuration(0.5, delay: 0, usingSpringWithDamping: 1.0,
            initialSpringVelocity: 0, options: .CurveEaseInOut, animations: {
            self.mainViewController.view.frame.origin.x = targetPosition
            }, completion: completion)
    }
    
    //给主页面边缘添加、取消阴影
    func showShadowForMainViewController(shouldShowShadow: Bool) {
        if (shouldShowShadow) {
            mainViewController.view.layer.shadowOpacity = 0.8
        } else {
            mainViewController.view.layer.shadowOpacity = 0.0
        }
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
}
 
// 菜单状态枚举
enum MenuState {
    case Collapsed  // 未显示(收起)
    case Expanding   // 展开中
    case Expanded   // 展开
}
源码下载:  hangge_1028.zip

5,功能改进
如果只有左右滑动能调出菜单的话,会显得把菜单功能隐藏太深,可能用户使用半天还不知道有这个菜单。
所以通常除了滑动调出菜单,页面上也会提供个菜单按钮,一般放置在导航栏上。点击按钮同样可以打开,收起菜单。效果图如下:
   

(1)在StoryBoard中,点击首页面(Main)的Scene,选择Editor -> Embed In -> Navigation Controller 添加导航控制器

(2)设置导航控制器的StoryBoard ID为 mainNavigaiton,同时给主页面的导航栏左侧添加一个菜单按钮。

(3)ViewController.swift 代码如下(高亮处为修改过的地方):

import UIKit
 
class ViewController: UIViewController {
    // 主页导航控制器
    var mainNavigationController:UINavigationController!
    
    // 主页面控制器
    var mainViewController:MainViewController!
    
    // 菜单页控制器
    var menuViewController:MenuViewController?
    
    // 菜单页当前状态
    var currentState = MenuState.Collapsed {
        didSet {
            //菜单展开的时候,给主页面边缘添加阴影
            let shouldShowShadow = currentState != .Collapsed
            showShadowForMainViewController(shouldShowShadow)
        }
    }
    
    // 菜单打开后主页在屏幕右侧露出部分的宽度
    let menuViewExpandedOffset: CGFloat = 60
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        //初始化主视图
        mainNavigationController = UIStoryboard(name: "Main", bundle: nil)
            .instantiateViewControllerWithIdentifier("mainNavigaiton")
            as! UINavigationController
        view.addSubview(mainNavigationController.view)
        
        //指定Navigation Bar左侧按钮的事件
        mainViewController = mainNavigationController.viewControllers.first
            as! MainViewController
        mainViewController.navigationItem.leftBarButtonItem?.action = Selector("showMenu")
        
        //添加拖动手势
        let panGestureRecognizer = UIPanGestureRecognizer(target: self,
            action: "handlePanGesture:")
        mainNavigationController.view.addGestureRecognizer(panGestureRecognizer)
        
        //单击收起菜单手势
        let tapGestureRecognizer = UITapGestureRecognizer(target: self,
            action: "handlePanGesture")
        mainNavigationController.view.addGestureRecognizer(tapGestureRecognizer)
    }
    
    //导航栏左侧按钮事件响应
    func showMenu() {
        //如果菜单是展开的则会收起,否则就展开
        if currentState == .Expanded {
            animateMainView(false)
        }else {
            addMenuViewController()
            animateMainView(true)
        }
    }
    
    //拖动手势响应
    func handlePanGesture(recognizer: UIPanGestureRecognizer) {
        
        switch(recognizer.state) {
            // 刚刚开始滑动
        case .Began:
            // 判断拖动方向
            let dragFromLeftToRight = (recognizer.velocityInView(view).x > 0)
            // 如果刚刚开始滑动的时候还处于主页面,从左向右滑动加入侧面菜单
            if (currentState == .Collapsed && dragFromLeftToRight) {
                currentState = .Expanding
                addMenuViewController()
            }
            
            // 如果是正在滑动,则偏移主视图的坐标实现跟随手指位置移动
        case .Changed:
            let positionX = recognizer.view!.frame.origin.x +
                recognizer.translationInView(view).x
            //页面滑到最左侧的话就不许要继续往左移动
            recognizer.view!.frame.origin.x = positionX < 0 ? 0 : positionX
            recognizer.setTranslation(CGPointZero, inView: view)
            
            // 如果滑动结束
        case .Ended:
            //根据页面滑动是否过半,判断后面是自动展开还是收缩
            let hasMovedhanHalfway = recognizer.view!.center.x > view.bounds.size.width
            animateMainView(hasMovedhanHalfway)
        default:
            break
        }
    }
    
    //单击手势响应
    func handlePanGesture() {
        //如果菜单是展开的点击主页部分则会收起
        if currentState == .Expanded {
            animateMainView(false)
        }
    }
    
    // 添加菜单页
    func addMenuViewController() {
        if (menuViewController == nil) {
            menuViewController = UIStoryboard(name: "Main", bundle: nil)
                .instantiateViewControllerWithIdentifier("menuView") as? MenuViewController
            
            // 插入当前视图并置顶
            view.insertSubview(menuViewController!.view, atIndex: 0)
            
            // 建立父子关系
            addChildViewController(menuViewController!)
            menuViewController!.didMoveToParentViewController(self)
        }
    }
    
    //主页自动展开、收起动画
    func animateMainView(shouldExpand: Bool) {
        // 如果是用来展开
        if (shouldExpand) {
            // 更新当前状态
            currentState = .Expanded
            // 动画
            animateMainViewXPosition(CGRectGetWidth(mainNavigationController.view.frame) -
                menuViewExpandedOffset)
        }
            // 如果是用于隐藏
        else {
            // 动画
            animateMainViewXPosition(0) { finished in
                // 动画结束之后s更新状态
                self.currentState = .Collapsed
                // 移除左侧视图
                self.menuViewController?.view.removeFromSuperview()
                // 释放内存
                self.menuViewController = nil;
            }
        }
    }
    
    //主页移动动画(在x轴移动)
    func animateMainViewXPosition(targetPosition: CGFloat,
        completion: ((Bool) -> Void)! = nil) {
            //usingSpringWithDamping:1.0表示没有弹簧震动动画
            UIView.animateWithDuration(0.5, delay: 0, usingSpringWithDamping: 1.0,
                initialSpringVelocity: 0, options: .CurveEaseInOut, animations: {
                    self.mainNavigationController.view.frame.origin.x = targetPosition
                }, completion: completion)
    }
    
    //给主页面边缘添加、取消阴影
    func showShadowForMainViewController(shouldShowShadow: Bool) {
        if (shouldShowShadow) {
            mainNavigationController.view.layer.shadowOpacity = 0.8
        } else {
            mainNavigationController.view.layer.shadowOpacity = 0.0
        }
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
}
 
// 菜单状态枚举
enum MenuState {
    case Collapsed  // 未显示(收起)
    case Expanding   // 展开中
    case Expanded   // 展开
}

时间: 2024-09-27 09:39:31

Swift 侧滑菜单的主页向右滑动,露出下方菜单页实现例子的相关文章

Swift侧滑菜单实现仿QQ,菜单带缩放效果

前面我写了一篇文章介绍如何实现侧滑菜单:Swift - 侧滑菜单的实现(样例1:主页向右滑动,露出下方菜单页) 其实现方式是,通过手势拖动主页面移动,从而露出下面的菜单页(其实后面的菜单页是固定不动的). 下面演示另一种样式的实现(模仿手机QQ的侧滑菜单),主页面滑动停靠的过程中会逐渐缩小,同时菜单页也会逐渐移动放大,浮现出来. (注:本文样例是基于前面文章的demo修改的,如果没阅读前文的话可以先去看下.为便于理解,下面将效果分两步实现.) 1,主页停靠侧边时尺寸逐渐缩小 (1)定义了新属性

class-VB怎么做才能把combo装饰成类似菜单条那样的下拉菜单的效果呢?

问题描述 VB怎么做才能把combo装饰成类似菜单条那样的下拉菜单的效果呢? VB怎么做才能把combo装饰成类似菜单条那样的下拉菜单的效果呢?关于combo和菜单的结合怎么实现? 解决方案 这可以用toolbar代替menu,把combo装入菜单条里面.

求大神解答一下-div+css的下拉菜单怎么让初始状态时所有菜单都是收起来的

问题描述 div+css的下拉菜单怎么让初始状态时所有菜单都是收起来的 div+css的下拉菜单怎么让初始状态时所有菜单都是收起来的不是进去第一个是展开的 解决方案 初始时让菜单所在的div隐藏,待需要显示时用js改变这个div的显示状态. div + css 只能做出静态的页面,页面如同一个图片,不能与用户交互,通过js事件改变css的值来实现交互. 解决方案二: css统一设置菜单display:none 你要展开那个设置dom元素的style为display:block显示或者写个高级别样

Flex/AS3/flash player支持屏蔽右键菜单,自定义菜单,并设置相应的菜单事件(示例,图解)

Flex/AS3/flash player支持屏蔽右键菜单,自定义菜单,并设置相应的菜单事件(示例,图解) 播放器 版本 11.2以后支持右键菜单屏蔽及自定义菜单 1.更新播放器 ,11.2 以上版本 http://download.macromedia.com/get/flashplayer/updaters/11/playerglobal11_3.swc http://download.macromedia.com/get/flashplayer/updaters/11/playerglob

求救!如何查看记事本菜单id,如另存为这个菜单的id

问题描述 求救!如何查看记事本菜单id,如另存为这个菜单的id 如题,毕设做一个VC的程序要用到,各位路过的大侠帮帮忙啊!spy++可以吗,怎么用呢 解决方案 用VS的打开文件(不是打开项目)打开记事本的exe,就可以看到了,如果是VC6的话好像记得要选择按资源方式 解决方案二: 就是打开文件,然后再找到C:windowssystem32里面的那个notepad.exe是吗,我打开那个文件,但是资源视图里什么都看不到啊,怎么办呢,麻烦你告诉我一下,感激不尽啊

java-当调用右键菜单以后,怎么才能再次使用编辑菜单?或者说让编辑菜单恢复?

问题描述 当调用右键菜单以后,怎么才能再次使用编辑菜单?或者说让编辑菜单恢复? import java.awt.CheckboxMenuItem; import java.awt.Dimension; import java.awt.Frame; import java.awt.Menu; import java.awt.MenuBar; import java.awt.MenuItem; import java.awt.MenuShortcut; import java.awt.Panel;

MDI窗口的菜单怎么把文档的菜单放在MDI的子窗口上?

问题描述 MDI窗口的菜单怎么把文档的菜单放在MDI的子窗口上? MDI窗口的菜单怎么把文档的菜单放在MDI的子窗口上?怎么让主窗口和子窗口的菜单条分开显示? 解决方案 https://technet.microsoft.com/zh-cn/library/ms171659(v=vs.80).aspx 解决方案二: MDI 取得childFrame子框架的menu菜单

java web-从一个系统菜单跳到另外一个系统菜单,该如何实现

问题描述 从一个系统菜单跳到另外一个系统菜单,该如何实现 现在有个需求,需要从现在的系统中跳到以前的一个系统的一个功能下,两个系统都是通过struts实现页面跳转,能不能在XML配置文件中直接配置另外一个系统的某个功能的action,这个action该怎么配置?望指点指点. 解决方案 如果两个系统不涉及数据交换,或者通过数据库交换数据,你菜单的href直接写那个系统的地址就可以了. 如果两个系统登录共享,可以用单点登录:http://zghbwjl.blog.163.com/blog/stati

谁做过能编辑的树形菜单,有没有能编辑的jquery树形菜单插件,或者其他的JS菜单也行

问题描述 谁做过能编辑的树形菜单,有没有能编辑的jquery树形菜单插件,或者其他的JS菜单也行 解决方案 解决方案二: 解决方案三:http://www.ztree.me/v3/demo.php#_101感觉比easyui的好一点解决方案四:ztree和easyui的都可以.但是ztree要好点解决方案五:ztree和easyui都行吧··不过我都是用easyui的可编辑算是树菜单一个控件都会有的吧