zoukankan      html  css  js  c++  java
  • iOS开发——高级篇——iOS抽屉效果实现原理

    实现一个简单的抽屉效果:

    核心思想:KVO实现监听mainV的frame值的变化

    核心代码:

    #import "ViewController.h"
    
    // @"frame"
    
    #define XMGkeyPath(objc, keyPath) @(((void)objc.keyPath, #keyPath))
    
    // 在宏里面如果在参数前添加了#,就会把参数变成C语言字符串
    
    // 获取屏幕的宽度
    #define screenW [UIScreen mainScreen].bounds.size.width
    
    // 获取屏幕的高度
    #define screenH [UIScreen mainScreen].bounds.size.height
    
    @interface ViewController ()
    
    
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        // 添加所有的子控件
        [self setUpAllChildView];
        
        // 添加拖拽手势
        UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
        
        [_mainV addGestureRecognizer:pan];
        
        // KVO作用:时刻监听某个对象的某个属性的改变
        // _main frame属性的改变
        // Observer:观察者
        // KeyPath:监听的属性
        // NSKeyValueObservingOptionNew:表示监听新值的改变
        [_mainV addObserver:self forKeyPath:XMGkeyPath(_mainV, frame) options:NSKeyValueObservingOptionNew context:nil];
        
        // 给控制器的view添加一个点按
        
        UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tap)];
        
        [self.view addGestureRecognizer:tap];
        
        
    }
    
    - (void)tap
    {
        if (_mainV.frame.origin.x != 0) {
            // 把_mainV还原最开始的位置
            
            [UIView animateWithDuration:0.25 animations:^{
                _mainV.frame = self.view.bounds;
                
            }];
            
        }
    }
    
    // 只要监听的属性一改变,就会调用
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
    {
        if (_mainV.frame.origin.x > 0) { // 往右滑动,显示左边控件,隐藏右边控件
            _rightV.hidden = YES;
        }else if (_mainV.frame.origin.x < 0){ // 往左滑动,显示右边控件
            _rightV.hidden = NO;
        }
    }
    
    // 注意:当对象被销毁的时候,一定要注意移除观察者
    - (void)dealloc
    {
        // 移除观察者
        [_mainV removeObserver:self forKeyPath:XMGkeyPath(_mainV, frame)];
    }
    
    #define targetR 300
    
    #define targetL -230
    
    - (void)pan:(UIPanGestureRecognizer *)pan
    {
        // 获取手势的偏移量
        CGPoint transP = [pan translationInView:_mainV];
        
        // 获取x轴的偏移量,相对于上一次
        CGFloat offsetX = transP.x;
        
        // 修改最新的main.frame,
        _mainV.frame = [self frameWithOffsetX:offsetX];
        
        // 复位
        [pan setTranslation:CGPointZero inView:_mainV];
        
        // 判断下当前手指有没有抬起,表示手势结束
        if (pan.state == UIGestureRecognizerStateEnded) { // 手指抬起,定位
            // x>屏幕的一半,定位到右边某个位置
            CGFloat target = 0;
            if (_mainV.frame.origin.x > screenW * 0.5) {
                target = targetR;
            }else if (CGRectGetMaxX(_mainV.frame) < screenW * 0.5){
                // 最大的x < 屏幕一半的时候,定义到左边某个位置
                target = targetL;
            }
            
            
            // 获取x轴的偏移量
            CGFloat offsetX = target - _mainV.frame.origin.x;
            
            [UIView animateWithDuration:0.25 animations:^{
                
                _mainV.frame = [self frameWithOffsetX:offsetX];
            }];
            
        }
    }
    
    #define XMGMaxY 100
    
    // 给定一个x轴的偏移量计算下最新main的frame
    - (CGRect)frameWithOffsetX:(CGFloat)offsetX
    {
        
        // 获取当前main的frame
        CGRect frame = _mainV.frame;
        
        // 计算当前的x,y,w,h
        // 获取最新的x
        CGFloat x = frame.origin.x + offsetX;
        
        // 获取最新的y
        CGFloat y = x / screenW * XMGMaxY;
        
        // 当用户往左边移动的时候,_main.x < 0,y需要增加,为正
        if (frame.origin.x < 0) {
            y = -y;
        }
        
        // 获取最新的h
        CGFloat h = screenH - 2 * y;
        
        // 获取缩放比例
        CGFloat scale = h / screenH;
        
        // 获取最新的w
        CGFloat w = screenW * scale;
        
        
        return CGRectMake(x, y, w, h);
    }
    
    
    
    // 添加所有的子控件
    - (void)setUpAllChildView
    {
        // left
        UIView *leftV = [[UIView alloc] initWithFrame:self.view.bounds];
        leftV.backgroundColor = [UIColor greenColor];
        [self.view addSubview:leftV];
        _leftV = leftV;
        
        // right
        UIView *rightV = [[UIView alloc] initWithFrame:self.view.bounds];
        rightV.backgroundColor = [UIColor blueColor];
        [self.view addSubview:rightV];
        _rightV = rightV;
        
        // main
        UIView *mainV = [[UIView alloc] initWithFrame:self.view.bounds];
        mainV.backgroundColor = [UIColor redColor];
        [self.view addSubview:mainV];
        _mainV = mainV;
    }
    
    @end
    

    用法:

    继承ViewController

    实现如下代码即可:

    #import "SlideViewController.h"
    
    @interface SlideViewController ()
    
    @end
    
    @implementation SlideViewController
    
    - (void)viewDidLoad{
        [super viewDidLoad];
        NSLog(@"%s",__func__);
        
        // 创建一个tableView控制器
        UITableViewController *tableVc = [[UITableViewController alloc] init];
        
        tableVc.view.frame = self.mainV.bounds;
        
        [self.mainV addSubview:tableVc.view];
        
        // 设计原理,如果A控制器的view成为b控制器view的子控件,那么这个A控制器必须成为B控制器的子控制器
        [self addChildViewController:tableVc];
        
        
        
        UIViewController *VC = [[UIViewController alloc] init];
        
        UIImageView *imagev = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"胸小别讲话"]];
        
        //    imagev.contentMode = UIViewContentModeScaleAspectFill;
        
        imagev.frame = VC.view.frame;
        
        [VC.view addSubview:imagev];
        
        [self.rightV addSubview:VC.view];
        
        [self addChildViewController:VC];
        
        
        
        
    }
    
    @end
    

     github地址:https://github.com/chglog/drawer

  • 相关阅读:
    Python
    QinQ 技术解析
    TLS/SSL 协议
    TLS/SSL 协议
    TLS/SSL 协议
    排序算法之基本排序算法(冒泡、插入、选择)
    Spring Boot 学习笔记--手写版
    mysql -- collection一对多查询
    mybatis 批量操作增删改查
    easyUI之datagrid绑定后端返回数据的两种方式
  • 原文地址:https://www.cnblogs.com/chglog/p/9133278.html
Copyright © 2011-2022 走看看