首先看看效果图:
参考的源码来源于:TWTSideMenuViewController
源码信息来源于:iOS 7侧边栏菜单解决方案
最近的一个计划是看别人的源码,从别人的项目中学习。
首先引起我兴趣的是这个仿iOS 7后台的侧边菜单,于是果断下了源码,在明白了其原理后,仿照原来的Demo进行了一些简化和扩展,并封装成一个自己的JCSideMenuViewController类。
下面直接从我改造的JCSideMenuViewController的代码入手,讲解下原项目的原理和我从中学习到的一些优秀的思想和知识。
首先来看看JCSideMenuViewController的初始化过程,包括init方法和viewDidLoad方法。代码如下:
#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]; }
为了防止崩溃,在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 <objc/runtime.h>
@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 <NSObject> @optional - (void)sideMenuViewControllerWillOpenMenu :(JCSideMenuViewController *)sideMenuViewController; - (void)sideMenuViewControllerDidOpenMenu :(JCSideMenuViewController *)sideMenuViewController; - (void)sideMenuViewControllerWillCloseMenu:(JCSideMenuViewController *)sideMenuViewController; - (void)sideMenuViewControllerDidCloseMenu :(JCSideMenuViewController *)sideMenuViewController; @end
/* 委托 */ @property (weak, nonatomic) id<JCSideMenuViewControllerDelegate> delegate;
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。
接下来还会看更多的项目和类库,看了以后会继续更新博客。
仿iOS 7后台侧边菜单源码分析和总结,布布扣,bubuko.com
原文:http://blog.csdn.net/u010962810/article/details/20618625