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

核心思想: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

iOS开发——高级篇——iOS抽屉效果实现原理的更多相关文章

  1. iOS开发——高级篇——iOS开发之网络安全密码学

    一.非对称加密 - RSA : + 公钥加密,私钥解密: + 私钥加密,公钥解密: + 只能通过因式分解来破解 二.对称加密 - DES - 3DES - AES (高级密码标准,美国国家安全局使用, ...

  2. iOS开发——高级篇——iOS 项目的目录结构

    最近闲来无事去面试一下iOS开发,让我感到吃惊的,面试官竟然问怎么分目录结构,还具体问每个子目录的文件名. 目录结构确实非常重要,面试官这么问,无疑是想窥探开发经验.清晰的目录结构,可让人一眼明白相应 ...

  3. iOS开发——高级篇——iOS涂鸦画板效果实现

    一个简单的绘图应用,模仿苹果自带软件备忘录里的涂鸦功能 核心代码 #import "DrawView.h" #import "DrawPath.h" @inte ...

  4. iOS开发——高级篇——iOS 强制退出程序APP代码

    1.先po代码 UIAlertView* alert = [[UIAlertView alloc] initWithTitle:self.exitapplication message:@" ...

  5. iOS开发——高级篇——iOS中如何选择delegate、通知、KVO(以及三者的区别)

      在开发IOS应用的时候,我们会经常遇到一个常见的问题:在不过分耦合的前提下,controllers[B]怎么进行通信.在IOS应用不断的出现三种模式来实现这种通信:1委托delegation2通知 ...

  6. iOS开发——高级篇——iOS中常见的设计模式(MVC/单例/委托/观察者)

    关于设计模式这个问题,在网上也找过一些资料,下面是我自己总结的,分享给大家 如果你刚接触设计模式,我们有好消息告诉你!首先,多亏了Cocoa的构建方式,你已经使用了许多的设计模式以及被鼓励的最佳实践. ...

  7. iOS开发——高级篇——iOS中为什么block用copy属性

    1. Block的声明和线程安全Block属性的声明,首先需要用copy修饰符,因为只有copy后的Block才会在堆中,栈中的Block的生命周期是和栈绑定的,可以参考之前的文章(iOS: 非ARC ...

  8. iOS开发——高级篇——iOS键盘的相关设置(UITextfield)

    一.键盘风格 UIKit框架支持8种风格键盘. typedef enum { UIKeyboardTypeDefault, // 默认键盘:支持所有字符 UIKeyboardTypeASCIICapa ...

  9. iOS开发——高级篇——iOS 中的 NSTimer

    以前的老代码在使用 NSTimer 时出现了内存泄露 NSTimer fire 我们先用 NSTimer 来做个简单的计时器,每隔5秒钟在控制台输出 Fire .比较想当然的做法是这样的: 1 2 3 ...

随机推荐

  1. docker run redis

    #拉取 Redis 镜像 C:\Users\WYJ>docker pull redis Using default tag: latest latest: Pulling from librar ...

  2. 特殊权限和facl

    目 录 第1章 FACL访问控制    1 1.1 FACL简介    1 1.2 getfacl命令查看acl权限    1 1.3 setfacl设置文件的cal权限    1 1.4 批量添加a ...

  3. day16-python之函数式编程匿名函数

    1.复习 #!/usr/bin/env python # -*- coding:utf-8 -*- name = 'alex' #name=‘lhf’ def change_name(): name= ...

  4. python_正则_re模块

    正则表达式元字符: 字符匹配 : . :除换行符以外的任意单个字符 [] :指定范围内字符 [^] :指定范围外字符 次数匹配: * :任意次,0,1,多次 .* :任意字符 任意次 ? :至多1次或 ...

  5. python 学习总结3

    Python蟒蛇绘制 一.实现程序如下 import turtle turtle.setup (650, 350, 200, 200)#turtle的绘图窗体turtle.setup(width, h ...

  6. SpringBoot log4j2 异常

    log4j 配置 <dependency> <groupId>org.springframework.boot</groupId> <artifactId&g ...

  7. POJ 2631 Roads in the North (求树的直径)

    Description Building and maintaining roads among communities in the far North is an expensive busine ...

  8. 【HDU 6153】A Secret (KMP)

    Problem Description Today is the birthday of SF,so VS gives two strings S1,S2 to SF as a present,whi ...

  9. PHP include和require 区别

    require 的使用方法如 require("MyRequireFile.php"); .这个函数通常放在 PHP 程序的最前面,PHP 程序在执行前,就会先读入 require ...

  10. how-do-i-access-windows-event-viewer-log-data-from-java

    https://stackoverflow.com/questions/310355/how-do-i-access-windows-event-viewer-log-data-from-java