iOS开发基础87-抽屉效果
1.iOS开发基础77-一像素线的几种实现方式2.iOS开发基础78-iOS 国际化3.iOS开发基础82-关于iOS目录4.iOS开发基础81-Runtime实战5.iOS开发基础80-关于Xcode86.iOS开发基础79-强制退出程序7.iOS开发基础90-密码学8.iOS开发基础89-Runloop9.iOS开发基础88-涂鸦效果
10.iOS开发基础87-抽屉效果
11.iOS开发基础86-FMDB12.iOS开发基础85-线程dispatch apply13.iOS开发基础84-HTTP请求方法详解与增删改查的应用14.iOS开发基础83-线程组15.iOS开发基础106-Instruments16.iOS开发基础105-Xcode收集Crashs的各种方法17.iOS开发基础104-正向代理和反向代理18.iOS开发基础103-APP之间跳转19.iOS开发基础102-后台保活方案20.iOS开发基础101-指纹和面部识别21.iOS开发基础100-MDM证书申请流程22.iOS开发基础99-iOS 内购的防范与优化23.iOS开发基础98-跳转淘宝案例24.iOS开发基础97-应用内购(In-App Purchase)的安全性解析与收据处理流程25.iOS开发基础96-UI类继承关系图26.iOS开发基础95-程序内评价27.iOS开发基础94-xcode1028.iOS开发基础93-GCD死锁29.iOS开发基础92-线程保活30.iOS开发基础91-线程同步技术与资源共享详解31.iOS开发基础138-视频编码32.iOS开发基础137-音视频编解码简介33.iOS开发基础136-防暴力点击34.iOS开发基础135-Core Data35.iOS开发基础134-异步并行上传问题36.iOS开发基础133-崩溃预防37.iOS开发基础132-POSIX线程库38.iOS开发基础131-isa指针39.iOS开发基础130-视频录制上传40.iOS开发基础129-音频录制上传41.iOS开发基础128-应用本地化42.iOS开发基础127-深入探讨KVO43.iOS开发基础126-深入探索设计模式44.iOS开发基础125-深入探索SDWebImage45.iOS开发基础124-RunLoop实现卡顿检测46.iOS开发基础123-自动释放池原理47.iOS开发基础122-RunLoop48.iOS开发基础121-APP启动优化49.iOS开发基础120-通知与线程50.iOS开发基础119-组件化51.iOS开发基础118-Runtime52.iOS开发基础117-Hybrid53.iOS开发基础116-性能监控54.iOS开发基础115-Socket55.iOS开发基础114-YYCache56.iOS开发基础113-Unity3D57.iOS开发基础112-GCD常见场景58.iOS开发基础111-RAC59.iOS开发基础110-Core Graphics应用场景60.iOS开发基础109-网络安全61.iOS开发基础108-常见的编程范式62.iOS开发基础107-iOS直播63.iOS开发基础148-ABM vs MDM64.iOS开发基础147-ABM集中管理Apple设备65.iOS开发基础146-深入解析WKWebView66.iOS开发基础145-Apple Search Ads67.iOS开发基础144-逐字打印效果68.iOS开发基础143-性能优化69.iOS开发基础142-广告归因70.iOS开发基础141-音频解码71.iOS开发基础140-音频编码72.iOS开发基础139-视频解码73.iOS开发基础149-由UUIDString引发的思考实现一个简单的抽屉效果:
核心思想:KVO实现监听mainV的frame值的变化
核心代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 | #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
实现如下代码即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | #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
将来的你会感谢今天如此努力的你!
版权声明:本文为博主原创文章,未经博主允许不得转载。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· PowerShell开发游戏 · 打蜜蜂
· 凌晨三点救火实录:Java内存泄漏的七个神坑,你至少踩过三个!