☰
RESideMenu 集成与深度解析:在 iOS 中实现带视差效果的侧滑菜单
2026/10/15 15:25:33 网站建设 项目流程
  • 移动开发
  • UI组件

【免费下载链接】RESideMenu

iOS 7/8 style side menu with parallax effect.

项目地址:https://gitcode.com/gh_mirrors/re/RESideMenu
点击查看免费下载

本文以 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 声明,使用本库需要满足以下条件:

项目要求
Xcode6.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 可对照查看:

  1. 创建RESideMenu的子类。示例中命名为DEMORootViewController,并在头文件中#import "RESideMenu.h"、声明遵循RESideMenuDelegate协议(见 DEMORootViewController.h)。
  2. 在 Storyboard 中把根视图的 owner(File's Owner)指定为DEMORootViewController。
  3. 向 Storyboard 添加更多视图控制器,并分别设置 Storyboard ID 为"leftMenuViewController"、"rightMenuViewController"和"contentViewController"(新版 Xcode 中该字段位于 Identity inspector,名为 "Storyboard ID")。
  4. 在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.

项目地址:https://gitcode.com/gh_mirrors/re/RESideMenu
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询