iOS:抽屜側滑動畫兩種形式(1、UIView側滑 2、ViewController側滑)


前言:

在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

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM