- 移动开发
- UI组件
【免费下载链接】RESideMenu
iOS 7/8 style side menu with parallax effect.
本文以 RESideMenu 开源库为核心,系统讲解如何在 iOS 6.0+ 项目中集成一套具备 iOS 7/8 风格、带 parallax(视差)效果的左右侧滑菜单,覆盖 CocoaPods 与手动安装、纯代码与 Storyboards 两种接入方式、全部可定制属性及其默认值,并从源码层面剖析其手势识别、缩放/平移动画与 UIMotionEffect 视差效果的实现原理。读完本文,你将能够独立在任意 iOS 工程中落地一个可配置、可回调、支持左右双菜单的侧滑框架,并理解其内部工作机制。
项目概览与核心能力
RESideMenu 是一个基于 UIKit 实现的侧滑菜单容器控制器,由 RESideMenu/RESideMenu.m 中的RESideMenu类(UIViewController子类)作为根容器,通过容器内嵌(child view controller)方式组织内容页与菜单页。其设计灵感源自 Dribbble 上的两个设计稿(Instasave iPhone App 与 Social Feed iOS7),呈现 iOS 7/8 风格的扁平化视觉语言。
库的核心特性可以归纳为三点:
- 左右双菜单:自 4.0 版本起,内容视图控制器左右两侧可以各挂载一个菜单视图控制器(
leftMenuViewController与rightMenuViewController),二者可同时存在、独立呈现。 - Parallax 视差效果:菜单容器与内容容器分别挂载
UIInterpolatingMotionEffect,在用户倾斜设备时产生不同位移速率的层次感。 - 手势驱动:内置
UIPanGestureRecognizer,支持从屏幕边缘或任意位置拖拽开合菜单,并伴有缩放、淡入淡出、弹性回弹等过渡动画。
仓库自带两套完整可运行的示例工程:Examples/Simple/RESideMenuExample(纯代码构建)与 Examples/Storyboards/RESideMenuStoryboardsExample(Storyboards 集成),可直接在 Xcode 中运行查看效果。
环境要求
根据 README.md 与 RESideMenu.podspec 声明,使用本库需要满足以下条件:
| 项目 | 要求 |
|---|---|
| Xcode | 6.0 或更高 |
| 编译器 | Apple LLVM compiler |
| iOS 最低版本 | 6.0(podspec中s.platform = :ios, '6.0') |
| 内存管理 | ARC(s.requires_arc = true) |
| 依赖框架 | QuartzCore(阴影绘制需要) |
需要说明的是,部分效果(如UIInterpolatingMotionEffect视差、preferredStatusBarStyle状态栏控制)依赖 iOS 7+ 运行时,源码通过IF_IOS7_OR_GREATER(...)宏(定义于 RESideMenu/RECommonFunctions.m)做了版本保护,因此低版本系统上会自动降级,不影响编译与基本功能。
Demo 演示
克隆仓库后,直接用 Xcode 打开并运行RESideMenuExample工程即可看到纯代码集成的完整效果;如需体验 Storyboards 集成方式,则构建运行RESideMenuStoryboardsExample。两个示例共用同一套视觉素材(位于各自的Images.xcassets中,包括菜单背景图MenuBackground、内容页大图Balloon、背景星图Stars等)。
以下为库的运行效果截图与动态演示(源文件位于仓库根目录):
在动态演示中可以看到:内容页向右平移并缩放至 0.7 倍让出左侧菜单;背景图保持放大状态形成景深;菜单项切换时通过setContentViewController:animated:完成内容页替换。这些行为全部由默认属性值驱动,详见后文“定制化”一节。
安装方式
方式一:CocoaPods(推荐)
README 推荐通过 CocoaPods 包管理器安装,以获得灵活的依赖管理与极简的接入流程。官方建议使用CocoaPods >= 0.28.0,并通过 Homebrew 安装Git >= 1.8.0。
# 安装 CocoaPods(如尚未安装) $ [sudo] gem install cocoapods $ pod setup切换到你的 Xcode 工程目录并创建 Podfile:
$ cd /path/to/MyProject $ touch Podfile $ edit Podfile在 Podfile 中声明依赖(最低 iOS 版本与 README 保持一致):
platform :ios, '6.0' pod 'RESideMenu', '~> 4.0.7'然后安装:
$ pod install安装完成后,务必通过生成的.xcworkspace文件(而非原来的.xcodeproj)打开工程:
$ open MyProject.xcworkspace排错提示:若pod install失败,多半是本地 Git 版本低于 CocoaPods 的预期。可执行git --version确认版本不低于 1.8.0;需要完整排查时,用pod install --verbose查看详细日志。
方式二:手动安装
手动集成非常简单:将仓库中 RESideMenu 目录下的全部文件拖入工程,并在需要使用它的类头部#include "RESideMenu.h"(或#import)即可。该目录共 6 个源文件:
RESideMenu.h/RESideMenu.m:核心容器控制器与全部公开接口;UIViewController+RESideMenu.h/.m:为任意UIViewController提供sideMenuViewController属性与两个 IBAction 快捷方法;RECommonFunctions.h/.m:工具宏与 UIKit 扁平模式检测函数。
注意手动集成时需自行开启 ARC,并确保工程链接了 QuartzCore 框架(CocoaPods 方式下 podspec 已自动声明)。
纯代码快速接入
第一步:在 AppDelegate 中创建容器并装配控制器
在- (BOOL)application:didFinishLaunchingWithOptions:中创建内容控制器、左右菜单控制器,并组装为RESideMenu根容器:
// 创建内容与菜单控制器 // DEMONavigationController *navigationController = [[DEMONavigationController alloc] initWithRootViewController:[[DEMOHomeViewController alloc] init]]; DEMOLeftMenuViewController *leftMenuViewController = [[DEMOLeftMenuViewController alloc] init]; DEMORightMenuViewController *rightMenuViewController = [[DEMORightMenuViewController alloc] init]; // 创建侧滑菜单容器 // RESideMenu *sideMenuViewController = [[RESideMenu alloc] initWithContentViewController:navigationController leftMenuViewController:leftMenuViewController rightMenuViewController:rightMenuViewController]; sideMenuViewController.backgroundImage = [UIImage imageNamed:@"Stars"]; // 设置为根控制器 // self.window.rootViewController = sideMenuViewController;该初始化方法声明于 RESideMenu/RESideMenu.h,其实现(RESideMenu.m)只是保存三个子控制器引用,真正的视图装配发生在viewDidLoad:左右菜单视图被加入menuViewContainer,内容视图被加入contentViewContainer,并通过addChildViewController:建立标准容器包含关系,保证子控制器的生命周期回调(viewWillAppear:等)正常触发。
实际示例中(DEMOAppDelegate.m)还配置了阴影与状态栏样式,可直接参考。这里backgroundImage仅是演示,你完全可以不设置背景图,让菜单控制器自身的背景色直接铺满屏幕——前提是不要同时设置菜单控制器的背景为透明色。
第二步:在任意控制器中唤起菜单
得益于UIViewController+RESideMenu类别(UIViewController+RESideMenu.m),任意处于容器内的控制器都能通过self.sideMenuViewController拿到容器引用。该属性的实现会沿parentViewController链向上查找,直到找到RESideMenu实例。
呈现左侧菜单:
[self.sideMenuViewController presentLeftMenuViewController];呈现右侧菜单:
[self.sideMenuViewController presentRightMenuViewController];这两个方法内部(RESideMenu.m)先重置菜单容器状态,再分别调用showLeftMenuViewController/showRightMenuViewController播放动画。另外类别还提供了两个 IBAction 方法presentLeftMenuViewController:与presentRightMenuViewController:,可直接绑定到UIBarButtonItem或 Storyboard 按钮的 action——DEMOFirstViewController.m(Examples/Simple/RESideMenuExample/DEMOFirstViewController.m)正是用导航栏左右按钮触发这两个 action 的。
第三步:切换内容页并收起菜单
菜单项点击后,通常需要切换到新的内容控制器并隐藏菜单:
#import <RESideMenu/RESideMenu.h> [self.sideMenuViewController setContentViewController:viewController animated:YES]; [self.sideMenuViewController hideMenuViewController];setContentViewController:animated:的源码实现(RESideMenu.m)会先以透明淡入方式挂载新内容视图,动画结束后移除旧控制器并更新阴影与视差效果,从而避免生硬的跳转。左右菜单示例中(如 DEMOLeftMenuViewController.m)均采用“切换内容 + 收起菜单”的组合写法。
Storyboards 集成
如果你习惯用 Storyboards 组织界面,README 给出了完整的五步接入方案,示例工程 DEMORootViewController 可对照查看:
- 创建
RESideMenu的子类。示例中命名为DEMORootViewController,并在头文件中#import "RESideMenu.h"、声明遵循RESideMenuDelegate协议(见 DEMORootViewController.h)。 - 在 Storyboard 中把根视图的 owner(File's Owner)指定为
DEMORootViewController。 - 向 Storyboard 添加更多视图控制器,并分别设置 Storyboard ID 为
"leftMenuViewController"、"rightMenuViewController"和"contentViewController"(新版 Xcode 中该字段位于 Identity inspector,名为 "Storyboard ID")。 - 在
DEMORootViewController.m中覆写awakeFromNib,用 Storyboard ID 实例化三个子控制器:
- (void)awakeFromNib { self.contentViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"contentViewController"]; self.leftMenuViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"leftMenuViewController"]; self.rightMenuViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"rightMenuViewController"]; }补充:iOS 8+ 的免代码方案。源码 RESideMenu.m 在__IPHONE_8_0条件下提供了三个IBInspectable字符串属性——contentViewStoryboardID、leftMenuViewStoryboardID、rightMenuViewStoryboardID(声明见 RESideMenu.h)。在 iOS 8 及以上运行时,可以直接在 Storyboard 的 Identity inspector 中填写这三个 Storyboard ID,awakeFromNib会自动完成实例化,从而省去第 4 步的手写代码。
定制化:全部可配置属性与默认值
RESideMenu的全部公开属性声明于 RESideMenu/RESideMenu.h,默认值统一在commonInit(RESideMenu.m)中初始化。下表为完整清单(含 README 未列出、但源码中存在的fadeMenuView与contentViewFadeOutAlpha,它们对动画效果有直接影响):
@property (assign, readwrite, nonatomic) NSTimeInterval animationDuration; // 动画时长,默认 0.35 秒 @property (strong, readwrite, nonatomic) UIImage *backgroundImage; // 菜单背景图(可 nil) @property (assign, readwrite, nonatomic) BOOL panGestureEnabled; // 是否启用滑动手势,默认 YES @property (assign, readwrite, nonatomic) BOOL panFromEdge; // 是否仅从屏幕边缘触发,默认 YES @property (assign, readwrite, nonatomic) NSUInteger panMinimumOpenThreshold; // 最小滑动打开阈值,默认 60.0 @property (assign, readwrite, nonatomic) BOOL interactivePopGestureRecognizerEnabled; // 是否兼容导航控制器返回手势,默认 YES @property (assign, readwrite, nonatomic) BOOL scaleContentView; // 内容页是否缩放,默认 YES @property (assign, readwrite, nonatomic) BOOL scaleBackgroundImageView; // 背景图是否缩放,默认 YES @property (assign, readwrite, nonatomic) BOOL scaleMenuView; // 菜单是否缩放,默认 YES @property (assign, readwrite, nonatomic) BOOL fadeMenuView; // 菜单是否淡入淡出,默认 YES @property (assign, readwrite, nonatomic) BOOL contentViewShadowEnabled; // 内容页阴影开关,默认 NO @property (assign, readwrite, nonatomic) UIColor *contentViewShadowColor; // 阴影颜色,默认 blackColor @property (assign, readwrite, nonatomic) CGSize contentViewShadowOffset; // 阴影偏移,默认 CGSizeZero @property (assign, readwrite, nonatomic) CGFloat contentViewShadowOpacity; // 阴影不透明度,默认 0.4 @property (assign, readwrite, nonatomic) CGFloat contentViewShadowRadius; // 阴影半径,默认 8.0 @property (assign, readwrite, nonatomic) CGFloat contentViewFadeOutAlpha; // 内容页淡出目标透明度,默认 1.0(即不淡出) @property (assign, readwrite, nonatomic) CGFloat contentViewScaleValue; // 内容页缩放比例,默认 0.7 @property (assign, readwrite, nonatomic) CGFloat contentViewInLandscapeOffsetCenterX; // 横屏时内容页右移量,默认 30 @property (assign, readwrite, nonatomic) CGFloat contentViewInPortraitOffsetCenterX; // 竖屏时内容页右移量,默认 30 @property (assign, readwrite, nonatomic) CGFloat parallaxMenuMinimumRelativeValue; // 菜单视差最小值,默认 -15 @property (assign, readwrite, nonatomic) CGFloat parallaxMenuMaximumRelativeValue; // 菜单视差最大值,默认 15 @property (assign, readwrite, nonatomic) CGFloat parallaxContentMinimumRelativeValue; // 内容视差最小值,默认 -25 @property (assign, readwrite, nonatomic) CGFloat parallaxContentMaximumRelativeValue; // 内容视差最大值,默认 25 @property (assign, readwrite, nonatomic) CGAffineTransform menuViewControllerTransformation; // 菜单初始形变,默认缩放 1.5 倍 @property (assign, readwrite, nonatomic) BOOL parallaxEnabled; // 视差开关,默认 YES @property (assign, readwrite, nonatomic) BOOL bouncesHorizontally; // 是否允许水平弹性回弹,默认 YES @property (assign, readwrite, nonatomic) UIStatusBarStyle menuPreferredStatusBarStyle; // 菜单展开时状态栏样式 @property (assign, readwrite, nonatomic) BOOL menuPrefersStatusBarHidden; // 菜单展开时是否隐藏状态栏属性背后对应的实现行为
这些属性并非摆设,其效果在 RESideMenu.m 中逐一对位:
scaleContentView/contentViewScaleValue:showLeftMenuViewController(L285-L300)中,若scaleContentView为 YES,内容容器被缩放为contentViewScaleValue(默认 0.7);同时内容页向右平移contentViewInPortraitOffsetCenterX / contentViewInLandscapeOffsetCenterX(+屏幕宽度)形成经典抽屉效果;右菜单展开时则向左平移负偏移量。scaleBackgroundImageView:viewDidLoad与动画过程中,背景图默认被放大 1.7 倍(CGAffineTransformMakeScale(1.7f, 1.7f)),收放时与内容页联动,制造景深错觉。scaleMenuView/menuViewControllerTransformation:菜单容器默认以 1.5 倍缩放开始,随手势逐渐还原为 1.0,形成“菜单从后方放大浮现”的层次感。contentViewShadowEnabled及阴影四件套:updateContentViewShadow(L454-L465)通过 CALayer 的shadowPath/shadowColor/shadowOffset/shadowOpacity/shadowRadius为内容容器绘制阴影,示例工程将其与contentViewScaleValue搭配使用,视觉上强化内容页“悬浮”于菜单之上。parallaxEnabled及四个相对值:addMenuViewControllerMotionEffects与addContentViewControllerMotionEffects(L480-L522)为菜单容器与内容容器分别挂载水平/垂直两个UIInterpolatingMotionEffect,倾斜设备时位移幅度不同(内容 ±25、菜单 ±15),正是 parallax 视差的核心来源。注意该效果仅在 iOS 7+ 运行时生效(受IF_IOS7_OR_GREATER宏保护)。panFromEdge:在gestureRecognizer:shouldReceiveTouch:(L538-L545)中,当菜单未展开时,只有触摸点落在距屏幕左右边缘 20pt 以内的区域才交给侧滑手势处理;设为 NO 后全屏任意位置均可拖拽。panMinimumOpenThreshold:手势结束判定(L675-L705)中,若内容页位移不足该阈值(默认 60pt)则自动收回菜单,超过则按滑动速度与方向决定展开/收起,并兼顾了bouncesHorizontally对回弹边界的约束。interactivePopGestureRecognizerEnabled:当内容控制器是UINavigationController且其栈内页面多于 1 个时,shouldReceiveTouch:会让位于系统自带边缘返回手势,避免与侧滑菜单冲突(L529-L536)。
使用背景图的注意事项
README 明确提醒:若设置了backgroundImage,请务必将菜单视图控制器的背景色设为 clear color,否则背景图会被菜单自身的背景完全遮挡。这正是示例中DEMOLeftMenuViewController采用透明背景的原因。
RESideMenuDelegate 生命周期回调
RESideMenu支持通过RESideMenuDelegate协议(RESideMenu.h)监听菜单的完整生命周期。协议方法全部为@optional,实现后即可收到如下消息:
- (void)sideMenu:(RESideMenu *)sideMenu didRecognizePanGesture:(UIPanGestureRecognizer *)recognizer; - (void)sideMenu:(RESideMenu *)sideMenu willShowMenuViewController:(UIViewController *)menuViewController; - (void)sideMenu:(RESideMenu *)sideMenu didShowMenuViewController:(UIViewController *)menuViewController; - (void)sideMenu:(RESideMenu *)sideMenu willHideMenuViewController:(UIViewController *)menuViewController; - (void)sideMenu:(RESideMenu *)sideMenu didHideMenuViewController:(UIViewController *)menuViewController;各回调的触发时机与源码对应关系:
| 回调方法 | 触发时机 | 源码位置 |
|---|---|---|
didRecognizePanGesture: | 每次手势事件(含拖动过程中)都会回调,便于做自定义联动 | RESideMenu.m |
willShowMenuViewController: | 菜单即将显示:按钮调用或手势首次越过判定线时 | RESideMenu.m 与 L634-L646 |
didShowMenuViewController: | 展开动画完成之后 | RESideMenu.m |
willHideMenuViewController: | 收起动画开始之前 | RESideMenu.m |
didHideMenuViewController: | 收起动画完成之后 | RESideMenu.m |
在 DEMOAppDelegate.m 中,AppDelegate 自身实现了该协议,在每个回调里打印menuViewController的类名,是理解回调顺序的最佳调试样例。
状态栏外观管理
容器还接管了菜单展开期间的状态栏外观。preferredStatusBarStyle、prefersStatusBarHidden与preferredStatusBarUpdateAnimation(RESideMenu.m)三个方法根据visible状态在“菜单样式”与“内容页样式”之间切换:
- 菜单展开时使用
menuPreferredStatusBarStyle(例如UIStatusBarStyleLightContent,示例中通过menuPreferredStatusBarStyle = 1设置)与menuPrefersStatusBarHidden; - 菜单收起时回归
contentViewController自身的状态栏样式; - 每次状态切换通过
statusBarNeedsAppearanceUpdate触发setNeedsStatusBarAppearanceUpdate刷新。
源码结构速览
- RESideMenu/RESideMenu.h:公开 API、全部属性、初始化方法与 delegate 协议;
- RESideMenu/RESideMenu.m:容器实现、手势逻辑、动画与视差、状态栏管理(共 860 行);
- RESideMenu/UIViewController+RESideMenu.h 与 .m:全局便捷访问类别;
- RESideMenu/RECommonFunctions.h 与 .m:
IF_IOS7_OR_GREATER宏与 UIKit 扁平模式检测; - RESideMenu.podspec:CocoaPods 元数据(版本 4.0.7、iOS 6.0+、ARC、QuartzCore);
- Examples/Simple/RESideMenuExample:纯代码示例;
- Examples/Storyboards/RESideMenuStoryboardsExample:Storyboards 示例。
许可证
RESideMenu 基于 MIT 许可证开源(LICENSE),Copyright © 2013 Roman Efimov。使用者可自由使用、修改、分发,并需保留版权声明;软件按“AS IS”提供,作者不对任何损失承担责任。完整许可条款见仓库 LICENSE 文件。
- 移动开发
- UI组件
【免费下载链接】RESideMenu
iOS 7/8 style side menu with parallax effect.
相关推荐
未来已来:GR00T-H-N1.7开启医疗机器人新纪元,50+全球机构的共同选择
未来已来:GR00T H N1.7开启医疗机器人新纪元,50+全球机构的共同选择 GR00T H N1.7是NVIDIA Isaac GR00T N1.7的术后
PicoRV32代码实现原理:从Verilog模块到功能验证
PicoRV32代码实现原理:从Verilog模块到功能验证 PicoRV32是一款高度优化的RISC V CPU实现,以其超小的面积和可配置特性成为嵌入式系统
硬件开发指令集嵌入式TimLiu-iOS侧滑菜单终极指南:MMDrawerController与RESideMenu实战
TimLiu iOS侧滑菜单终极指南:MMDrawerController与RESideMenu实战 TimLiu iOS是iOS开发常用三方库、插件、知名博客
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考