首先看看效果圖:


參考的源碼來源於:TWTSideMenuViewController
源碼信息來源於:iOS 7側邊欄菜單解決方案
最近的一個計劃是看別人的源碼,從別人的項目中學習。<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4KPHA+ytfPyNL9xvDO0tDLyKS1xMrH1eK49rfCaU9TIDe688yotcSy4LHfssu1paOs09rKx7n7ts/PwsHL1LTC66Os1NrD97DXwcvG5NStwO2686Ost8LV1dStwLS1xERlbW+9+NDQwcvSu9CpvPK7r7rNwKnVuaOssqK34tews8nSu7j219S8urXESkNTaWRlTWVudVZpZXdDb250cm9sbGVywOChozwvcD4KPHA+z8LD5taxvdO0087SuMTU7LXESkNTaWRlTWVudVZpZXdDb250cm9sbGVytcS0+sLryOvK1qOsvbK94s/C1K3P7sS/tcTUrcDtus3O0rTT1tDRp8+wtb21xNK70KnTxdDjtcTLvM/rus3Wqsq2oaM8L3A+CjxwPjxicj4KPC9wPgo8cD48YnI+CjwvcD4KPGgxPrP1yry7r0pDU2lkZU1lbnVWaWV3Q29udHJvbGxlcjwvaDE+CjxwPsrXz8jAtL+0v7RKQ1NpZGVNZW51Vmlld0NvbnRyb2xsZXK1xLP1yry7r7n9s8yjrLD8wKhpbml0t723qLrNdmlld0RpZExvYWS3vbeooaO0+sLryOfPwqO6PC9wPgo8cD48L3A+CjxwcmUgY2xhc3M9"brush:java;">#pragma mark - Initialization
- (instancetype)initWithLeftMenuViewController:(UIViewController *)lMenuViewController
MainViewController:(UIViewController *)aMainViewController
RightMenuViewController:(UIViewController *)rMenuViewController
{
self = [super init];
if (self) {
self.leftMenuViewController = lMenuViewController;
self.rightMenuViewController = rMenuViewController;
self.mainViewController = aMainViewController;
}
return self;
}
#pragma mark - Life cycle
- (void)viewDidLoad {
[super viewDidLoad];
// 初始化基本參數
self.zoomScale = kZoomScale;
self.openingSide = kMiddleSide;
self.edgeOffset = (UIOffset) {
.horizontal = (IS_IPHONE ? kHorizontaliPhoneOffset : kHorizontaliPadOffset),
.vertical = 0.0
};
// 添加左邊的菜單視圖控制器
if (self.leftMenuViewController) {
[self addChildViewController:self.leftMenuViewController];
[self.view addSubview:self.leftMenuViewController.view];
[self.leftMenuViewController didMoveToParentViewController:self];
self.leftMenuViewController.sideMenuViewController = self;
self.leftMenuViewController.view.hidden = YES;
}
// 添加右邊的菜單視圖控制器
if (self.rightMenuViewController) {
[self addChildViewController:self.rightMenuViewController];
[self.view addSubview:self.rightMenuViewController.view];
[self.rightMenuViewController didMoveToParentViewController:self];
self.rightMenuViewController.sideMenuViewController = self;
self.rightMenuViewController.view.hidden = YES;
}
// 添加主視圖控制器
if (self.mainViewController) {
[self addChildViewController:self.mainViewController];
[self.view addSubview:self.mainViewController.view];
[self.mainViewController didMoveToParentViewController:self];
self.mainViewController.sideMenuViewController = self;
}
// 添加一個平移手勢,用於關閉菜單視圖
_panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panToCloseMenu)];
[self.view addGestureRecognizer:_panGesture];
}
在JCSideViewController的初始化方法中,有三個UIViewController作為參數被傳入,其中leftMenuViewController,rightMenuViewController和mainViewController分別表示左邊菜單欄的視圖控制器,右邊菜單欄的視圖控制器和主視圖控制器,而背後控制這些控制器的就是我們的JCSideMenuViewController。由於開發者不一定會同時定制雙邊的菜單欄,所以允許leftMenuViewController或rightMenuViewController為空。
為了防止崩潰,在viewDidLoad方法中首先要判斷這幾個視圖控制器是否為空。
在viewDidLoad方法中,我們做的就是將三個視圖控制器的視圖添加到JCSideMenuViewController的根視圖上,而初始的MenuViewControllers的視圖均設置為隱藏。
而後面添加的平移手勢panGesture的作用是通過平移手勢來關閉菜單。
接下來講解下打開菜單的方法,代碼如下:
- (void)openMenuInSide:(JCSide)aSide Animated:(BOOL)animated Completion:(void (^)(BOOL))completion {
if (self.open) {
return;
}
if ([self.delegate respondsToSelector:@selector(sideMenuViewControllerWillOpenMenu:)]) {
[self.delegate sideMenuViewControllerWillOpenMenu:self];
}
self.open = YES;
self.openingSide = aSide;
[self makeEnlargeTransformForMenuView];
[self showCurrentMenuView];
void (^openMenuBlock)(void) = ^{
[self makeRestoreTransformForMenuView];
self.mainViewController.view.transform = [self openTransformForView:self.mainViewController.view];
};
void (^openCompleteBlock)(BOOL) = ^(BOOL finished) {
if (finished) {
[self addOverlayButtonToScaleViewController];
[self updateStatusBarStyle];
}
if ([self.delegate respondsToSelector:@selector(sideMenuViewControllerDidOpenMenu:)]) {
[self.delegate sideMenuViewControllerDidOpenMenu:self];
}
if (completion) {
completion(finished);
}
};
if (animated) {
[UIView animateWithDuration:kOpenMenuAnimationDuration
delay:kOpenMenuAnimationDelay
options:UIViewAnimationOptionCurveEaseInOut
animations:openMenuBlock
completion:openCompleteBlock];
}
else {
openMenuBlock();
openCompleteBlock(YES);
}
}
下面是動畫的執行過程:
1.在執行打開菜單動畫之前,首先放大並顯示菜單欄視圖:
[self makeEnlargeTransformForMenuView];
[self showCurrentMenuView]; void (^openMenuBlock)(void) = ^{
[self makeRestoreTransformForMenuView];
self.mainViewController.view.transform = [self openTransformForView:self.mainViewController.view];
}; void (^openCompleteBlock)(BOOL) = ^(BOOL finished) {
if (finished) {
[self addOverlayButtonToScaleViewController];
[self updateStatusBarStyle];
}
if ([self.delegate respondsToSelector:@selector(sideMenuViewControllerDidOpenMenu:)]) {
[self.delegate sideMenuViewControllerDidOpenMenu:self];
}
if (completion) {
completion(finished);
}
}; if (animated) {
[UIView animateWithDuration:kOpenMenuAnimationDuration
delay:kOpenMenuAnimationDelay
options:UIViewAnimationOptionCurveEaseInOut
animations:openMenuBlock
completion:openCompleteBlock];
}
else {
openMenuBlock();
openCompleteBlock(YES);
}
對應打開菜單,當然有個關閉菜單的方法了,其實就是打開菜單動畫的逆向過程。代碼如下:
- (void)closeMenuAnimated:(BOOL)animated completion:(void (^)(BOOL))completion {
if (!self.open) {
return;
}
if ([self.delegate respondsToSelector:@selector(sideMenuViewControllerWillCloseMenu:)]) {
[self.delegate sideMenuViewControllerWillCloseMenu:self];
}
self.open = NO;
[self removeOverlayButtonFromScaleViewController];
void (^closeMenuBlock)(void) = ^{
self.mainViewController.view.transform = CGAffineTransformIdentity;
[self makeEnlargeTransformForMenuView];
};
void (^closeCompleteBlock)(BOOL) = ^(BOOL finished) {
if (finished) {
[self updateStatusBarStyle];
}
[self makeRestoreTransformForMenuView];
[self hideCurrentMenuView];
self.openingSide = kMiddleSide;
if ([self.delegate respondsToSelector:@selector(sideMenuViewControllerDidCloseMenu:)]) {
[self.delegate sideMenuViewControllerDidCloseMenu:self];
}
if (completion) {
completion(finished);
}
};
if (animated) {
[UIView animateWithDuration:kCloseMenuAnimationDuration
delay:kCloseMenuAnimationDelay
options:UIViewAnimationOptionCurveEaseInOut
animations:closeMenuBlock
completion:closeCompleteBlock];
}
else {
closeMenuBlock();
closeCompleteBlock(YES);
}
}
注意到上面的代碼中調用了幾個子方法,下面是其代碼實現:
- (CGAffineTransform)enlargeTransformForMenuView {
if (self.openingSide == kLeftSide) { // 如果是左邊的菜單視圖,那麼先擴大,再向左平移一定距離
CGFloat scaleSize = 1.0f / self.zoomScale;
CGAffineTransform scaleTransform = CGAffineTransformScale(self.leftMenuViewController.view.transform,
scaleSize,
scaleSize);
return CGAffineTransformTranslate(scaleTransform,
-self.openingSide * (self.view.frame.size.width / scaleSize),
0.0);
}
else if (self.openingSide == kRightSide) { // 如果是右邊的菜單視圖,那麼直接放大
return CGAffineTransformScale(self.rightMenuViewController.view.transform, 2.5f, 2.5f);
}
return CGAffineTransformIdentity;
}
- (void)makeEnlargeTransformForMenuView {
if (self.openingSide == kLeftSide) {
self.leftMenuViewController.view.transform = [self enlargeTransformForMenuView];
}
else if (self.openingSide == kRightSide) {
self.rightMenuViewController.view.transform = [self enlargeTransformForMenuView];
}
}
- (void)makeRestoreTransformForMenuView {
if (self.openingSide == kLeftSide) {
self.leftMenuViewController.view.transform = CGAffineTransformIdentity;
}
else if (self.openingSide == kRightSide) {
self.rightMenuViewController.view.transform = CGAffineTransformIdentity;
}
}
- (void)showCurrentMenuView {
if (self.openingSide == kLeftSide) {
self.leftMenuViewController.view.hidden = NO;
}
else if (self.openingSide == kRightSide) {
self.rightMenuViewController.view.hidden = NO;
}
}
- (void)hideCurrentMenuView {
if (self.openingSide == kLeftSide) {
self.leftMenuViewController.view.hidden = YES;
}
else if (self.openingSide == kRightSide) {
self.rightMenuViewController.view.hidden = YES;
}
}
- (CGAffineTransform)openTransformForView:(UIView *)view {
CGFloat transformSize = self.zoomScale;
CGAffineTransform curTransform = CGAffineTransformTranslate(view.transform,
self.openingSide *\
(CGRectGetMidX(view.bounds) + self.edgeOffset.horizontal),
self.openingSide *\
self.edgeOffset.vertical);
return CGAffineTransformScale(curTransform, transformSize, transformSize);
}
這裡要說明的一點是菜單欄的放大動畫,也就是上面代碼中的enlargeTransformForMenuView方法。
這裡分三種情況討論,一是打開的是左邊菜單欄,二是打開的是右邊菜單欄,三是不打開菜單欄。
在打開左邊菜單欄時,菜單視圖首先放大,然後左移一定的距離,以上變換均是基於原點是設備左下角的原點的坐標系。
如果打開的是右邊菜單欄,那麼菜單視圖只進行放大操作,如果右移一定的距離,那麼左邊必定漏出一段空隙來,參見下文圖中的紅色背景。由於想不到更好的解決方法,所以在這裡只能退而求其次,將按鈕向右移除視圖的動畫去掉了。(難怪原來的項目中沒有打開右邊菜單欄的示例)
另外還有個叫closeOverlayButton的家伙,在打開菜單後,main view controller的view將會縮小到一個角落裡,這時往上添加一個按鈕,點擊該按鈕可以實現關閉菜單(其實就是調用closeMenuAnimated:completion:方法),其完整代碼如下:
#pragma mark - Overlay button management
- (void)addOverlayButtonToScaleViewController {
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.accessibilityLabel = nil;
button.accessibilityHint = nil;
button.backgroundColor = [UIColor clearColor];
button.opaque = NO;
button.frame = self.mainViewController.view.frame;
[button addTarget:self action:@selector(closeButtonTouchUpInside) forControlEvents:UIControlEventTouchUpInside];
[button addTarget:self action:@selector(closeButtonTouchedDown) forControlEvents:UIControlEventTouchDown];
[button addTarget:self action:@selector(closeButtonTouchUpOutside) forControlEvents:UIControlEventTouchUpOutside];
[self.view addSubview:button];
_closeOverlayButton = button;
}
- (void)removeOverlayButtonFromScaleViewController {
[_closeOverlayButton removeFromSuperview];
}
- (void)closeButtonTouchUpInside {
[self closeMenuAnimated:YES completion:nil];
}
- (void)closeButtonTouchedDown {
_closeOverlayButton.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.4];
}
- (void)closeButtonTouchUpOutside {
_closeOverlayButton.backgroundColor = [UIColor clearColor];
}
由於不同菜單視圖的背景顏色不同,所以要對其狀態欄做一些適配。iOS 7的狀態欄主要分為兩種,一種是黑色,一種是白色,定義如下:
typedef NS_ENUM(NSInteger, UIStatusBarStyle) {
UIStatusBarStyleDefault = 0, // Dark content, for use on light backgrounds
UIStatusBarStyleLightContent NS_ENUM_AVAILABLE_IOS(7_0) = 1, // Light content, for use on dark backgrounds
UIStatusBarStyleBlackTranslucent NS_ENUM_DEPRECATED_IOS(2_0, 7_0, "Use UIStatusBarStyleLightContent") = 1,
UIStatusBarStyleBlackOpaque NS_ENUM_DEPRECATED_IOS(2_0, 7_0, "Use UIStatusBarStyleLightContent") = 2,
};#pragma mark - Status Bar management
- (UIViewController *)childViewControllerForStatusBarStyle {
if (!self.isOpen) {
return self.mainViewController;
}
else {
return (self.openingSide == kLeftSide) ? self.leftMenuViewController : self.rightMenuViewController;
}
}
- (UIViewController *)childViewControllerForStatusBarHidden {
if (!self.isOpen) {
return self.mainViewController;
}
else {
return (self.openingSide == kLeftSide) ? self.leftMenuViewController : self.rightMenuViewController;
}
}
- (void)updateStatusBarStyle {
if ([self respondsToSelector:@selector(setNeedsStatusBarAppearanceUpdate)]) {
[self setNeedsStatusBarAppearanceUpdate];
}
}
- (UIStatusBarStyle)preferredStatusBarStyle {
return UIStatusBarStyleLightContent;
}setNeesStatusBarAppearanceUpdate方法可以刷新狀態欄,通常在動畫執行完畢後調用。
在iPhone中,我們一般不需要擔心屏幕旋轉後的適配問題,因為大多數iPhone應用都支持一個方向。但是iPad應用就應該盡量考慮下屏幕旋轉後視圖的適配問題。代碼如下:
#pragma mark - Rotation
- (BOOL)shouldAutorotate {
return YES;
}
- (NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskAll;
}
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
if (self.open) {
[self removeOverlayButtonFromScaleViewController];
[UIView animateWithDuration:kRotationAnimationDuration animations:^{
[self makeEnlargeTransformForMenuView];
self.mainViewController.view.transform = CGAffineTransformIdentity;
} completion:nil];
}
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
if (self.open) {
[UIView animateWithDuration:kRotationAnimationDuration animations:^{
[self makeRestoreTransformForMenuView];
self.mainViewController.view.transform = [self openTransformForView:self.mainViewController.view];
} completion:^(BOOL finished) {
[self addOverlayButtonToScaleViewController];
}];
}
}在didRotate方法中執行動畫,將菜單欄還原,打開main view controller的view的動畫(縮小到一個角落)。
這樣可以才可以保證屏幕旋轉後動畫的執行。
現在將注意力放回到viewDidLoad方法的下列代碼中:
self.leftMenuViewController.sideMenuViewController = self; self.rightMenuViewController.sideMenuViewController = self; self.mainViewController.sideMenuViewController = self;
這裡的leftMenuViewController等三個控制器都包含一個sideMenuViewController的成員,並讓其指向self。那麼是不是每一個視圖控制器都要添加一個JCSideMenuViewController的屬性呢?哇靠,太麻煩了吧。沒錯,如果讓我來做的話,我只會這種方法。
但是原項目卻給出了一個非常好的解決方案:在JCSideMenuViewController頭文件中聲明一個UIViewController的Category,並在類別中將JCSideMenuViewController和UIViewController動態關聯起來。
首先要導入頭文件:
#import
@implementation UIViewController (SideMenu)
- (void)setSideMenuViewController:(JCSideMenuViewController *)sideMenuViewController {
objc_setAssociatedObject(self, @selector(sideMenuViewController), sideMenuViewController, OBJC_ASSOCIATION_ASSIGN);
}
- (JCSideMenuViewController *)sideMenuViewController {
JCSideMenuViewController *aSideMenuViewController = objc_getAssociatedObject(self, @selector(sideMenuViewController));
if (!aSideMenuViewController) {
aSideMenuViewController = self.parentViewController.sideMenuViewController;
}
return aSideMenuViewController;
}SDK中關於objc_setAssociatedObject函數的定義如下:
/**
* Sets an associated value for a given object using a given key and association policy.
*
* @param object The source object for the association.
* @param key The key for the association.
* @param value The value to associate with the key key for object. Pass nil to clear an existing association.
* @param policy The policy for the association. For possible values, see “Associative Object Behaviors.”
*
* @see objc_setAssociatedObject
* @see objc_removeAssociatedObjects
*/
OBJC_EXPORT void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
__OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_1);第二個參數key表示二者關聯的方式,在這裡我們用@selector(sideMenuViewController)將其關聯起來,該key用於獲取被關聯對象。
第三個參數表示要關聯的對象,也就是sideMenuViewController。
第四個參數表示關聯的策略,這裡使用的是OBJC_ASSOCIATION_ASSIGN。
接下來是獲取關聯對象的函數,在sdk中定義如下:
/**
* Returns the value associated with a given object for a given key.
*
* @param object The source object for the association.
* @param key The key for the association.
*
* @return The value associated with the key \e key for \e object.
*
* @see objc_setAssociatedObject
*/
OBJC_EXPORT id objc_getAssociatedObject(id object, const void *key)
__OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_1);
上面說的是如何將JCSideMenuViewController抽離成一個接口,供開發者調用並在自己的應用中添加以上側邊欄效果。
下面說的是如何在自己的項目中加入JCSideMenuViewController。
首先要定制一個自己的菜單欄視圖控制器,可以定制左邊的菜單欄、右邊的菜單欄,或者是雙邊的菜單欄。新建一個MenuViewController,然後定制菜單背景、菜單上的按鈕等。注意菜單背景要在viewDidLoad方法中調用addBackgroundImage方法進行配置:
(1)使用故事板定義背景視圖的情況
- (void)viewDidLoad {
[super viewDidLoad];
[self addBackgroundImageView:self.backgroundImageView];
}
(2)使用代碼添加背景視圖的情況
- (void)viewDidLoad
{
[super viewDidLoad];
UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"starsky.jpg"]];
[self addBackgroundImageView:imageView];
}addBackgroundImageView方法是UIViewController(SideMenu)類別中的一個方法,定義如下:
- (void)addBackgroundImage:(UIImage *)image {
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
[self addBackgroundImageView:imageView];
}
- (void)addBackgroundImageView:(UIImageView *)imageView {
[imageView removeFromSuperview];
imageView.frame = [[UIScreen mainScreen] bounds];
imageView.contentMode = UIViewContentModeScaleToFill;
// 不允許將AutoresizingMask轉換成Autolayout
imageView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view insertSubview:imageView atIndex:0];
}
為了明顯一點,我故意將JCSideMenuViewController.view的背景顏色設為紅色。可以看到在執行菜單欄的放大左移動畫時,右邊空了一大截出來。
在菜單視圖控制器中調用以上方法的關鍵是設置backgroundImageView的translatesAutoresizingMaskIntoConstraints屬性為NO,從而避免了以上位置偏移的問題出現。
另外,背景圖片的選取有一定的要求,最好選取568 * 320+或1024 * 768+的圖片。如果圖片尺寸不對,即使你設置圖片視圖的contentMode = UIViewContentModeScaleToFill,有時候還是不能將圖片鋪滿整個視圖。當然圖片尺寸合適,菜單欄的背景也好看很多。
在設置了背景以後,你可以直接在視圖上添加幾個按鈕,用代碼或故事板均可。
除此以外,由於每個菜單背景的顏色不同,所以要在MenuViewController下定制對應的狀態欄,方法如下:
- (UIStatusBarStyle)preferredStatusBarStyle {
// return UIStatusBarStyleDefault; // 菜單欄背景圖片為淺色,如白色,那麼令狀態欄的顏色為黑色
return UIStatusBarStyleLightContent; // 菜單欄背景圖片為深色,如黑色,那麼令狀態欄的顏色為白色
}
main view controller就是你的應用的入口。
在該控制器中添加一些按鈕,或者pan手勢,用來打開菜單。方法是調用sideMenuViewController中的openMenuInSide:Animated:completion:方法。
例如:
- (IBAction)openLeftMenu:(id)sender {
[self.sideMenuViewController openMenuInSide:kLeftSide Animated:YES Completion:nil];
}
- (IBAction)openRightMenu:(id)sender {
[self.sideMenuViewController openMenuInSide:kRightSide Animated:YES Completion:nil];
}
- (IBAction)panToOpenMenu:(id)sender {
CGPoint beginPoint = [self.panGesture locationInView:self.view];
CGPoint translation = [self.panGesture translationInView:self.view];
if (beginPoint.x <= self.view.bounds.size.width / 4 && translation.x > 0) {
[self.sideMenuViewController openMenuInSide:kLeftSide Animated:YES Completion:nil];
}
else if (beginPoint.x >= self.view.bounds.size.width * 3 / 4 && translation.x < 0) {
[self.sideMenuViewController openMenuInSide:kRightSide Animated:YES Completion:nil];
}
}
為什麼把創建JCSideMenuViewController對象放在最後呢?因為起碼要有了菜單欄視圖控制器和主視圖控制器以後,我們才有條件來初始化該類。在初始化後,記得將該控制器設置為整個應用程序的入口。代碼如下:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
LeftMenuViewController *lMenuViewController = [[UIStoryboard storyboardWithName:STORYBOARD_NAME bundle:nil]
instantiateViewControllerWithIdentifier:LEFT_MENU_VIEWCONTROLLER_ID];
RightMenuViewController *rMenuViewController = [[UIStoryboard storyboardWithName:STORYBOARD_NAME bundle:nil]
instantiateViewControllerWithIdentifier:RIGHT_MENU_VIEWCONTROLLER_ID];
UITabBarController *rootController = [[UIStoryboard storyboardWithName:STORYBOARD_NAME bundle:nil]
instantiateViewControllerWithIdentifier:ROOT_TABBAR_CONTROLLER];
JCSideMenuViewController *sideMenuViewController = [[JCSideMenuViewController alloc]
initWithLeftMenuViewController:lMenuViewController
MainViewController:rootController
RightMenuViewController:rMenuViewController];
self.window.rootViewController = sideMenuViewController;
return YES;
}
可以基於JCSideMenuViewController實現傳統的側邊菜單形式。
下面給出我的做法:
首先定義kZoomScale = 1.0(如果想使用原來的菜單樣式,只需要將下面的#if 0修改為#if 1)
#if 0
#define iOS7_BACKGROUND_SIDE_VIEW
#endif
#ifndef iOS7_BACKGROUND_SIDE_VIEW
static CGFloat const kZoomScale = 1.0;
#else
static CGFloat const kZoomScale = 0.5;
#endif
- (CGAffineTransform)enlargeTransformForMenuView {
#ifdef iOS7_BACKGROUND_SIDE_VIEW
if (self.openingSide == kLeftSide) { // 如果是左邊的菜單視圖,那麼先擴大,再向左平移一定距離
CGFloat scaleSize = 1.0f / self.zoomScale;
CGAffineTransform scaleTransform = CGAffineTransformScale(self.leftMenuViewController.view.transform,
scaleSize,
scaleSize);
return CGAffineTransformTranslate(scaleTransform,
-self.openingSide * (self.view.frame.size.width / scaleSize),
0.0);
}
else if (self.openingSide == kRightSide) { // 如果是右邊的菜單視圖,那麼直接放大
return CGAffineTransformScale(self.rightMenuViewController.view.transform, 2.5f, 2.5f);
}
return CGAffineTransformIdentity;
#else
if (self.openingSide == kLeftSide) {
return CGAffineTransformTranslate(self.leftMenuViewController.view.transform,
-self.openingSide * (self.view.frame.size.width / 2),
0.0);
}
else if (self.openingSide == kRightSide) {
return CGAffineTransformTranslate(self.rightMenuViewController.view.transform,
-self.openingSide * (self.view.frame.size.width / 2),
0.0);
}
return CGAffineTransformIdentity;
#endif
}實現效果如下:


當然,在菜單欄後面設置背景圖片會占用一定的內存(iPhone真機調試13M左右),所以最好還是使用一些透明背景比較好,節省內存而又不影響美觀。
最後還是附上源碼,交流學習。
JCSideMenuViewControllerDemo下載地址:點此進入下載頁
最後總結一下我從這個項目的源碼分析中學習到的一些知識:
1.結構體初始化:
self.edgeOffset = (UIOffset) {
.horizontal = (IS_IPHONE ? kHorizontaliPhoneOffset : kHorizontaliPadOffset),
.vertical = 0.0
};
2.在項目中使用委托方法:
@class JCSideMenuViewController; @protocol JCSideMenuViewControllerDelegate@optional - (void)sideMenuViewControllerWillOpenMenu :(JCSideMenuViewController *)sideMenuViewController; - (void)sideMenuViewControllerDidOpenMenu :(JCSideMenuViewController *)sideMenuViewController; - (void)sideMenuViewControllerWillCloseMenu:(JCSideMenuViewController *)sideMenuViewController; - (void)sideMenuViewControllerDidCloseMenu :(JCSideMenuViewController *)sideMenuViewController; @end
/* 委托 */ @property (weak, nonatomic) iddelegate;
3.使用UIViewController Category關聯類
這個是本次源碼分析的最大收獲,這確實是一個非常棒的設計模式,學習了。
4.仿射變換和animation方法
5.iOS 7的StatusBarStyle和屏幕旋轉後的視圖適配
6.判斷pan手勢的方向
pan手勢在入門時用過,當時也是一知半解,並且早就忘得七七八八了,而且當時也沒有寫博客記錄,幸好本次學習好好回顧了一下:
- (CGPoint)translationInView:(UIView *)view; // translation in the coordinate system of the specified view - (void)setTranslation:(CGPoint)translation inView:(UIView *)view; - (CGPoint)velocityInView:(UIView *)view; // velocity of the pan in pixels/second in the coordinate system of the specified view
其中translation記錄了pan手勢的平移軌跡,velocity記錄了pan手勢的速度。
7.translatesAutoresizingMaskIntoConstraints屬性的作用是禁止將視圖的AutoresizingMask轉換成Autolayout。
接下來還會看更多的項目和類庫,看了以後會繼續更新博客。