前言:
在iOS中抽屜動畫是很常用的一種技術,使用它有很炫的體驗效果,為app增添特色,形式就兩種,一個是UIView的側滑,另一個就是ViewController的側滑。
實現方式:
抽屜側滑動畫有三種方式,一種是設置UIView的frame實現動畫側滑;一種是使用MMDrawerController框架實現控制器的側滑;最后一種使用系統的UIScreenEdgePanGestureRecognizer實現側滑。
第一種方式:
UIView的側滑動畫,分別為leftView、mainView、rightView,主要給這三個view做處理。
例如,我們在用QQ時都會發現,消息列表向左滑動時,左側的功能界面被顯示出來,消息列表會拉到最右側, 就像一個抽屜拉出來一樣。除了QQ, 還有網易新聞等應用都采用了這樣的交互。本文就以此介紹簡易版的抽屜效果.
實現代碼如下:
(1)聲明三個View屬性
@interface ViewController () @property (nonatomic, strong) UIView *mainView; @property (nonatomic, strong) UIView *leftView; @property (nonatomic, strong) UIView *rightView; @end - (void)viewDidLoad { [super viewDidLoad]; [self creatChildView]; [self addGesture]; // 通過KVO來監聽mainView的frame的變化,從而判斷maimView是左滑還是右滑。 [_mainView addObserver:self forKeyPath:@"frame" options:NSKeyValueObservingOptionNew context:nil]; }
(2)創建View,在這注意view的添加順序, 我們想要讓最初顯示的view為mainView
- (void)creatChildView { self.leftView = [[UIView alloc] initWithFrame:self.view.bounds]; _leftView.backgroundColor = [UIColor greenColor]; [self.view addSubview:_leftView]; _rightView = [[UIView alloc] initWithFrame:self.view.bounds]; _rightView.backgroundColor = [UIColor blueColor]; [self.view addSubview:_rightView]; self.mainView = [[UIView alloc] initWithFrame:self.view.bounds]; _mainView.backgroundColor = [UIColor redColor]; [self.view addSubview:_mainView]; }
(3)給self.view添加一個平移手勢
- (void)addGesture { // 創建手勢 UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panAction:)]; [self.view addGestureRecognizer:pan]; }
(4)手勢觸發方法
- (void)panAction:(UIPanGestureRecognizer *)panGesture { // 獲取平移手勢移動后, 在相對坐標中的偏移量 CGPoint point = [panGesture translationInView:self.view]; // 聲明xNew變量用point.x賦值 CGFloat xNew = point.x; // 改變mainView的frame _mainView.frame = CGRectMake(xNew + self.mainView.frame.origin.x, 0, self.view.frame.size.width, self.view.frame.size.height); // 設置手勢移動后的point [panGesture setTranslation:CGPointZero inView:panGesture.view]; }
(5)只要被監聽的屬性發生變化, 就會觸發這個方法
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context { // 如果往右側移動, 顯示左邊,隱藏右邊 if (_mainView.frame.origin.x > 0) { _rightView.hidden = YES; // 往左移動,顯示右邊 } else if (_mainView.frame.origin.x < 0) { _rightView.hidden = NO; } }
實現效果如下:
結果總結:
這次實現的是簡易版的抽屜效果, 僅僅是用了三個view, 但在開發中一般為幾個不同的VC, 通過手勢實現抽屜效果, 同時還能設置一些動畫效果, 比如比較有名的MMDrawerController框架.要是項目有需要, 可以參考這個第三方. 如何實現幾個控制器間的抽屜效果, 下面接着就與大家分享.
第二種方式:
控制器抽屜側滑,左中右三個控制器,這里使用上面提到的MMDrawerController框架。
實現代碼如下:
1、首先創建三個控制器為center、left、right(我這里就簡寫了,具體的話大家可以下載Demo),創建完成之后,我們來到我們的AppDelegate,開始編寫我們的代碼了
- 1.1多話不說,先導入頭文件,並且添加一個MMDrawerController的屬性
//為MMDrawerController框架中 #import "MMDrawerController.h" #import "UIViewController+MMDrawerController.h" //為自己創建的三個控制器 #import "LitterLCenterViewController.h" #import "LitterLLeftViewController.h" #import "LitterLRightViewController.h" @interface LitterLAppDelegate () /** * MMDrawerController屬性 */ @property(nonatomic,strong) MMDrawerController * drawerController; @end
- 1.2 上面的做完后,我們便要顯示我們的窗口到設備上,接下來來到這里
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { //1、初始化控制器 UIViewController *centerVC = [[LitterLCenterViewController alloc]init]; UIViewController *leftVC = [[LitterLLeftViewController alloc]init]; UIViewController *rightVC = [[LitterLRightViewController alloc]init]; //2、初始化導航控制器 UINavigationController *centerNvaVC = [[UINavigationController alloc]initWithRootViewController:centerVC]; UINavigationController *leftNvaVC = [[UINavigationController alloc]initWithRootViewController:leftVC]; UINavigationController *rightNvaVC = [[UINavigationController alloc]initWithRootViewController:rightVC]; //3、使用MMDrawerController self.drawerController = [[MMDrawerController alloc]initWithCenterViewController:centerNvaVC leftDrawerViewController:leftNvaVC rightDrawerViewController:rightNvaVC]; //4、設置打開/關閉抽屜的手勢 self.drawerController.openDrawerGestureModeMask = MMOpenDrawerGestureModeAll; self.drawerController.closeDrawerGestureModeMask =MMCloseDrawerGestureModeAll; //5、設置左右兩邊抽屜顯示的多少 self.drawerController.maximumLeftDrawerWidth = 200.0; self.drawerController.maximumRightDrawerWidth = 200.0; //6、初始化窗口、設置根控制器、顯示窗口 self.window = [[UIWindow alloc]initWithFrame:[UIScreen mainScreen].bounds]; [self.window setRootViewController:self.drawerController]; [self.window makeKeyAndVisible]; return YES; }
2、完成上面后基本的抽屜效果就已經實現了,在這里的話,我們將要實現導航欄上面的按鈕,以及一些效果。
-
2.1、這里的話我們先實現導航欄的效果吧:
- 2.1.1、這里的話我用的是通過UIBarButtonItem的方法去實現的
//設置導航欄左右按鈕 self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc]initWithImage:[UIImage imageNamed:@"menu"] style:UIBarButtonItemStylePlain target:self action:@selector(leftBtn)]; self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]initWithImage:[UIImage imageNamed:@"navigationbar_friendattention"] style:UIBarButtonItemStylePlain target:self action:@selector(rightBtn)];
- 2.1.2、然而我們的作者給了我們一個類,里面有他通過QuartzCore繪制出來的按鈕,你們想看的話可以去看看
//首先得導入頭文件 #import "MMDrawerBarButtonItem.h" //---------------------------- self.navigationItem.leftBarButtonItem = [[MMDrawerBarButtonItem alloc]initWithTarget:self action:@selector(leftBtn)]; self.navigationItem.rightBarButtonItem = [[MMDrawerBarButtonItem alloc]initWithTarget:self action:@selector(rightBtn)];
- 2.1.3、這里的話就是我們的方法,其實很簡單(就一行代碼)但是過程很迷茫
//首先得導入頭文件 #import "UIViewController+MMDrawerController.h" //---------------------------- -(void)leftBtn{ //這里的話是通過遍歷循環拿到之前在AppDelegate中聲明的那個MMDrawerController屬性,然后判斷是否為打開狀態,如果是就關閉,否就是打開(初略解釋,里面還有一些條件) [self.mm_drawerController toggleDrawerSide:MMDrawerSideLeft animated:YES completion:nil]; } -(void)rightBtn{ [self.mm_drawerController toggleDrawerSide:MMDrawerSideRight animated:YES completion:nil]; }
- 2.2、完成上面后,導航欄的點擊就能切換,那么我們就來實現一個效果吧,所謂的彈簧效果,也就幾句代碼
//2、添加雙擊手勢 UITapGestureRecognizer * doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(doubleTap:)]; //2.1、雙擊 [doubleTap setNumberOfTapsRequired:2]; [self.view addGestureRecognizer:doubleTap]; //3、添加兩個手指雙擊手勢 UITapGestureRecognizer * twoFingerDoubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(twoFingerDoubleTap:)]; //3.1、雙擊 [twoFingerDoubleTap setNumberOfTapsRequired:2]; //3.2、兩個手指 默認為一個 [twoFingerDoubleTap setNumberOfTouchesRequired:2]; [self.view addGestureRecognizer:twoFingerDoubleTap]; //---------------------------- /** * 添加點擊手勢 一個手指雙擊 */ -(void)doubleTap:(UITapGestureRecognizer*)gesture{ [self.mm_drawerController bouncePreviewForDrawerSide:MMDrawerSideLeft completion:nil]; } /** * 添加點擊手勢 兩個個手指雙擊 */ -(void)twoFingerDoubleTap:(UITapGestureRecognizer*)gesture{ [self.mm_drawerController bouncePreviewForDrawerSide:MMDrawerSideRight completion:nil]; }
3、到這里的話就是最后一步了,第一設置數據源,第二實現協議了。
- 3.1、數據源的編寫,這里的話我用的都是靜態數據,就不做解釋了,右側和左側抽屜都為一樣的,你們自行查看吧
#pragma mark - UITableViewDataSource - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return 10; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (!cell) { cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]; } cell.textLabel.text =[NSString stringWithFormat:@"Left-Demo%ld",indexPath.row]; return cell; }
- 3.2、這里就是我們的最后一步,點擊Cell跳轉控制器了,那么我們的有一個控制器取名去:
LitterLShowViewController
#pragma mark - UITableViewDelegate -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ LitterLShowViewController *showVC = [[LitterLShowViewController alloc]init]; showVC.title = [NSString stringWithFormat:@"Left-Demo%ld",indexPath.row]; //拿到我們的LitterLCenterViewController,讓它去push UINavigationController* nav = (UINavigationController*)self.mm_drawerController.centerViewController; [nav pushViewController:showVC animated:NO]; //當我們push成功之后,關閉我們的抽屜 [self.mm_drawerController closeDrawerAnimated:YES completion:^(BOOL finished) { //設置打開抽屜模式為MMOpenDrawerGestureModeNone,也就是沒有任何效果。 [self.mm_drawerController setOpenDrawerGestureModeMask:MMOpenDrawerGestureModeNone]; }]; }
4、當我們的LitterLShowViewController退出后,我們的把打開抽屜模式在切換過來,當然這個是在中間控制器里面去寫,因為LitterLShowViewController退出后會呈現中間控制器
/** * 加載控制器的時候設置打開抽屜模式 (因為在后面會關閉) */ -(void)viewWillAppear:(BOOL)animated{ [super viewWillAppear:animated]; //設置打開抽屜模式 [self.mm_drawerController setOpenDrawerGestureModeMask:MMOpenDrawerGestureModeAll]; }
Demo地址:MMDrawerControllerDemo
gihub框架地址:https://github.com/mutualmobile/MMDrawerController
實現效果如下:
第三種方式:
使用UIScreenEdgePanGestureRecognizer寫側邊欄。
UIScreenEdgePanGestureRecognizer看起來像pan手勢,它是檢測屏幕邊緣的pan手勢的。系統在某些controller轉場的時候會使用這個手勢。你也可以使用這個手勢做其他的事情。
實現代碼如下:
#import "RootViewController.h" @interface RootViewController ()<UIGestureRecognizerDelegate> { CGFloat _centerX; CGFloat _centerY; UIView *_backgroundView; } @end
@implementation RootViewController - (void)viewDidLoad { [super viewDidLoad]; // 存儲坐標 _centerX = self.view.bounds.size.width / 2; _centerY = self.view.bounds.size.height / 2; self.view.backgroundColor = [UIColor blackColor]; // 屏幕邊緣pan手勢(優先級高於其他手勢) UIScreenEdgePanGestureRecognizer *leftEdgeGesture = \ [[UIScreenEdgePanGestureRecognizer alloc] initWithTarget:self action:@selector(handleLeftEdgeGesture:)]; leftEdgeGesture.edges = UIRectEdgeLeft; // 屏幕左側邊緣響應 [self.view addGestureRecognizer:leftEdgeGesture]; // 給self.view添加上 // 設置一個UIView用來替換self.view,self.view用來當做背景使用 _backgroundView = [[UIView alloc] initWithFrame:self.view.bounds]; _backgroundView.backgroundColor = [UIColor yellowColor]; [self.view addSubview:_backgroundView]; // 展示的view UIView *showView_01 = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 200)]; showView_01.tag = 0x1; showView_01.backgroundColor = [UIColor redColor]; [_backgroundView addSubview:showView_01]; }
// 手勢處理
- (void)handleLeftEdgeGesture:(UIScreenEdgePanGestureRecognizer *)gesture { // 獲取到當前被觸摸的view UIView *view = [self.view hitTest:[gesture locationInView:gesture.view] withEvent:nil]; NSLog(@"tag = %ld", (long)view.tag); if(UIGestureRecognizerStateBegan == gesture.state || UIGestureRecognizerStateChanged == gesture.state) { // 根據被觸摸手勢的view計算得出坐標值 CGPoint translation = [gesture translationInView:gesture.view]; NSLog(@"%@", NSStringFromCGPoint(translation)); NSLog(@"進行中"); // 進行設置 _backgroundView.center = CGPointMake(_centerX + translation.x, _centerY); } else { // 恢復設置 [UIView animateWithDuration:.3 animations:^{ _backgroundView.center = CGPointMake(_centerX, _centerY); }]; } } @end
代碼截圖如下:
效果圖如下:
提示如果想與其他手勢並發操作,實現如下代理即可:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { return YES; }
為原博主點贊吧:
http://www.jianshu.com/p/fd911eeda3a1
http://www.jianshu.com/p/9e55cbf7d5ab
https://yq.aliyun.com/articles/30676