首先看看效果圖:
參考的源碼來源於: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。
接下來還會看更多的項目和類庫,看了以後會繼續更新博客。