IOS APP 侧边栏滑动

import UIKit

// 此 View Controller 为根容器,本身并不包含任何 UI 元素
class ViewController: UIViewController {

// 该 TabBar Controller 不是传统意义上的容器,在此只负责提供 UITabBar 这个 UI 组件
var mainTabBarController: MainTabBarController!

// 主界面点击手势,用于在菜单划出状态下点击主页后自动关闭菜单
var tapGesture: UITapGestureRecognizer!

// 首页的 Navigation Bar 的提供者,是首页的容器
var homeNavigationController: UINavigationController!
// 首页中间的主要视图的来源
var homeViewController: HomeViewController!
// 侧滑菜单视图的来源
var leftViewController: LeftViewController!

// 构造主视图,实现 UINavigationController.view 和 HomeViewController.view 一起缩放
var mainView: UIView!

// 侧滑所需参数
var distance: CGFloat = 0
let FullDistance: CGFloat = 0.78
let Proportion: CGFloat = 0.77
var centerOfLeftViewAtBeginning: CGPoint!
var proportionOfLeftView: CGFloat = 1
var distanceOfLeftView: CGFloat = 50

// 侧滑菜单黑色半透明遮罩层
var blackCover: UIView!

override func viewDidLoad() {
    super.viewDidLoad()
    
    // 给根容器设置背景
    let imageView = UIImageView(image: UIImage(named: "back"))
    imageView.frame = UIScreen.mainScreen().bounds
    self.view.addSubview(imageView)
    
    // 通过 StoryBoard 取出左侧侧滑菜单视图
    leftViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("LeftViewController") as! LeftViewController
    // 适配 4.7 和 5.5 寸屏幕的缩放操作,有偶发性小 bug
    if Common.screenWidth > 320 {
        proportionOfLeftView = Common.screenWidth / 320
        distanceOfLeftView += (Common.screenWidth - 320) * FullDistance / 2
    }
    leftViewController.view.center = CGPointMake(leftViewController.view.center.x - 50, leftViewController.view.center.y)
    leftViewController.view.transform = CGAffineTransformScale(CGAffineTransformIdentity, 0.8, 0.8)
    
    // 动画参数初始化
    centerOfLeftViewAtBeginning = leftViewController.view.center
    // 把侧滑菜单视图加入根容器
    self.view.addSubview(leftViewController.view)
    
    // 在侧滑菜单之上增加黑色遮罩层,目的是实现视差特效
    blackCover = UIView(frame: CGRectOffset(self.view.frame, 0, 0))
    blackCover.backgroundColor = UIColor.blackColor()
    self.view.addSubview(blackCover)
    
    // 初始化主视图,即包含 TabBar、NavigationBar和首页的总视图
    mainView = UIView(frame: self.view.frame)
    // 初始化 TabBar
    let nibContents = NSBundle.mainBundle().loadNibNamed("MainTabBarController", owner: nil, options: nil)
    mainTabBarController = nibContents.first as! MainTabBarController
    // 取出 TabBar Controller 的视图加入主视图
    let tabBarView = mainTabBarController.view
    mainView.addSubview(tabBarView)
    // 从 StoryBoard 取出首页的 Navigation Controller
    homeNavigationController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("HomeNavigationController") as! UINavigationController
    // 从 StoryBoard 初始化而来的 Navigation Controller 会自动初始化他的 Root View Controller,即 HomeViewController
    // 我们将其(指针)取出,赋给容器 View Controller 的成员变量 homeViewController
    homeViewController = homeNavigationController.viewControllers.first as! HomeViewController
    // 分别将 Navigation Bar 和 homeViewController 的视图加入 TabBar Controller 的视图
    tabBarView.addSubview(homeViewController.navigationController!.view)
    tabBarView.addSubview(homeViewController.view)
    
    // 在 TabBar Controller 的视图中,将 TabBar 视图提到最表层
    tabBarView.bringSubviewToFront(mainTabBarController.tabBar)
    
    // 将主视图加入容器
    self.view.addSubview(mainView)
    
    // 分别指定 Navigation Bar 左右两侧按钮的事件
    homeViewController.navigationItem.leftBarButtonItem?.action = Selector("showLeft")
    homeViewController.navigationItem.rightBarButtonItem?.action = Selector("showRight")
    
    // 给主视图绑定 UIPanGestureRecognizer
    let panGesture = homeViewController.panGesture
    panGesture.addTarget(self, action: Selector("pan:"))
    mainView.addGestureRecognizer(panGesture)
    
    // 生成单击收起菜单手势
    tapGesture = UITapGestureRecognizer(target: self, action: "showHome")
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}

// 响应 UIPanGestureRecognizer 事件
func pan(recongnizer: UIPanGestureRecognizer) {
    let x = recongnizer.translationInView(self.view).x
    let trueDistance = distance + x // 实时距离
    let trueProportion = trueDistance / (Common.screenWidth*FullDistance)
    
    // 如果 UIPanGestureRecognizer 结束,则激活自动停靠
    if recongnizer.state == UIGestureRecognizerState.Ended {

        if trueDistance > Common.screenWidth * (Proportion / 3) {
            showLeft()
        } else if trueDistance < Common.screenWidth * -(Proportion / 3) {
            showRight()
        } else {
            showHome()
        }
        
        return
    }

    // 计算缩放比例
    var proportion: CGFloat = recongnizer.view!.frame.origin.x >= 0 ? -1 : 1
    proportion *= trueDistance / Common.screenWidth
    proportion *= 1 - Proportion
    proportion /= FullDistance + Proportion/2 - 0.5
    proportion += 1
    if proportion <= Proportion { // 若比例已经达到最小,则不再继续动画
        return
    }
    // 执行视差特效
    blackCover.alpha = (proportion - Proportion) / (1 - Proportion)
    // 执行平移和缩放动画
    recongnizer.view!.center = CGPointMake(self.view.center.x + trueDistance, self.view.center.y)
    recongnizer.view!.transform = CGAffineTransformScale(CGAffineTransformIdentity, proportion, proportion)
    
    // 执行左视图动画
    let pro = 0.8 + (proportionOfLeftView - 0.8) * trueProportion
    leftViewController.view.center = CGPointMake(centerOfLeftViewAtBeginning.x + distanceOfLeftView * trueProportion, centerOfLeftViewAtBeginning.y - (proportionOfLeftView - 1) * leftViewController.view.frame.height * trueProportion / 2 )
    leftViewController.view.transform = CGAffineTransformScale(CGAffineTransformIdentity, pro, pro)
}

// 封装三个方法,便于后期调用

// 展示左视图
func showLeft() {
    // 给首页 加入 点击自动关闭侧滑菜单功能
    mainView.addGestureRecognizer(tapGesture)
    // 计算距离,执行菜单自动滑动动画
    distance = self.view.center.x * (FullDistance*2 + Proportion - 1)
    doTheAnimate(self.Proportion, showWhat: "left")
    homeNavigationController.popToRootViewControllerAnimated(true)
}
// 展示主视图
func showHome() {
    // 从首页 删除 点击自动关闭侧滑菜单功能
    mainView.removeGestureRecognizer(tapGesture)
    // 计算距离,执行菜单自动滑动动画
    distance = 0
    doTheAnimate(1, showWhat: "home")
}
// 展示右视图
func showRight() {
    // 给首页 加入 点击自动关闭侧滑菜单功能
    mainView.addGestureRecognizer(tapGesture)
    // 计算距离,执行菜单自动滑动动画
    distance = self.view.center.x * -(FullDistance*2 + Proportion - 1)
    doTheAnimate(self.Proportion, showWhat: "right")
}
// 执行三种动画:显示左侧菜单、显示主页、显示右侧菜单
func doTheAnimate(proportion: CGFloat, showWhat: String) {
    UIView.animateWithDuration(0.3, delay: 0, options: UIViewAnimationOptions.CurveEaseInOut, animations: { () -> Void in
        // 移动首页中心
        self.mainView.center = CGPointMake(self.view.center.x + self.distance, self.view.center.y)
        // 缩放首页
        self.mainView.transform = CGAffineTransformScale(CGAffineTransformIdentity, proportion, proportion)
        if showWhat == "left" {
            // 移动左侧菜单的中心
            self.leftViewController.view.center = CGPointMake(self.centerOfLeftViewAtBeginning.x + self.distanceOfLeftView, self.leftViewController.view.center.y)
            // 缩放左侧菜单
            self.leftViewController.view.transform = CGAffineTransformScale(CGAffineTransformIdentity, self.proportionOfLeftView, self.proportionOfLeftView)
        }
        // 改变黑色遮罩层的透明度,实现视差效果
        self.blackCover.alpha = showWhat == "home" ? 1 : 0

        // 为了演示效果,在右侧菜单划出时隐藏漏出的左侧菜单,并无实际意义
        self.leftViewController.view.alpha = showWhat == "right" ? 0 : 1
        }, completion: nil)
}

}

import UIKit

// 侧滑菜单 View Controller
class LeftViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

let titlesDictionary = ["开通会员", "QQ钱包", "网上营业厅", "个性装扮", "我的收藏", "我的相册", "我的文件"]

@IBOutlet weak var settingTableView: UITableView!
@IBOutlet weak var avatarImageView: UIImageView!

@IBOutlet weak var heightLayoutConstraintOfSettingTableView: NSLayoutConstraint!

override func viewDidLoad() {
    super.viewDidLoad()
    
    settingTableView.delegate = self
    settingTableView.dataSource = self
    settingTableView.tableFooterView = UIView()
    
    heightLayoutConstraintOfSettingTableView.constant = Common.screenHeight < 500 ? Common.screenHeight * (568 - 221) / 568 : 347
    self.view.frame = CGRectMake(0, 0, 320 * 0.78, Common.screenHeight)
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}

// 处理点击事件
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    let viewController = Common.rootViewController
    viewController.homeViewController.titleOfOtherPages = titlesDictionary[indexPath.row]
    viewController.homeViewController.performSegueWithIdentifier("showOtherPages", sender: self)
    Common.contactsVC.view.removeFromSuperview()
    viewController.mainTabBarController.tabBar.hidden = true
    viewController.mainTabBarController.selectedIndex = 0
    viewController.showHome()
    tableView.deselectRowAtIndexPath(indexPath, animated: false)
}

func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return 7
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
    return 1
}

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("leftViewCell", forIndexPath: indexPath) 
    
    cell.backgroundColor = UIColor.clearColor()
    cell.textLabel!.text = titlesDictionary[indexPath.row]
    
    return cell
}

/*
// MARK: - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    // Get the new view controller using segue.destinationViewController.
    // Pass the selected object to the new view controller.
}
*/

}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,175评论 5 466
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,674评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,151评论 0 328
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,597评论 1 269
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,505评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,969评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,455评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,118评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,227评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,213评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,214评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,928评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,512评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,616评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,848评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,228评论 2 344
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,772评论 2 339

推荐阅读更多精彩内容