开篇作一下更新说明,当你看完这篇文章的时候,若是你以为文章里面的实现方案与需求不是那么合拍,请不要怀疑本身,由于我也这么以为。但其实主要目的仍是为了介绍一下runtime中一个不太经常使用的知识点,实现需求只是顺带,感谢盒子大佬的讲解,学到了不少东西。bash
iOS开发作了好几年,一直想写点东西却没有动手,一个是由于懒,还有一个就是我一个同事说的,我写啥?想写的别人都写过了。网络
事实上也确实是这样,不管你想知道什么知识点,几乎都能在网上找到答案,实在是没那个必要在后面跟风,关键是你尚未人家讲得好。async
可是凡事也有利弊,资料太多了不免选择恐惧症,随便搜一个组件化流程都能找到十几个不一样版本的方案,最可怕的是我还以为他们说的都对!这就很尴尬了。ide
更可悲的是,看了这么多的组件化教程,被安利了各类库以后,我依然没有把组件化学会。组件化
就比如听过了许多大道理,却依旧过很差这一辈子。记住了许多理论,却依然写很差代码。ui
这也是土系魔法讲义的由来,这个系列的每一篇文章都会更接地气一些,以一个具体需求为起始,用一种特殊的方式将文章的中心点讲述出来。spa
文章会以code和思路为主,讲解部分比较少,根据需求随时变动。线程
若是有什么地方说的不对,不用过来打我,我确定改。code
一个最基本的UIbutton的使用大概应该是这个样子的:orm
- (void)viewDidLoad {
[super viewDidLoad];
UIButton *testButton = [UIButton buttonWithType:UIButtonTypeCustom];
testButton.backgroundColor = [UIColor redColor];
testButton.frame = CGRectMake(100, 100, 100, 100);
[self.view addSubview:testButton];
[testButton addTarget:self action:@selector(test:) forControlEvents:UIControlEventTouchUpInside];
}
- (void)test:(UIButton*)sender{
NSLog(@"test");
}
复制代码
那么问题来了,想想,若是如今临时加一个需求,button响应事件以前要先获取相机视频权限,应该怎么作?(举个例子,一样的需求还有获取位置权限,检测网络链接,查看登陆状态等等)
先不说button,权限获取的代码大概应该长这样:
AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
if (authStatus == AVAuthorizationStatusRestricted || authStatus ==AVAuthorizationStatusDenied){
//啥也不干
}else if(authStatus == AVAuthorizationStatusNotDetermined){
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
if(granted){
dispatch_async(dispatch_get_main_queue(), ^{
//干正事儿
});
}
}];
}else{
//干正事儿
}
复制代码
须要注意的就一点,赞成获取权限的回调须要返回主线程。
还有你须要设置info.plist的Privacy。
提及来可能有些搞笑,很大一部分的工程中,上面那段代码就这冠冕堂皇的躺在- (void)test:(UIButton*)sender
方法里面。
固然这个写法实在是太...,除了萌新以外不多真的有人这么写了,可是其实上面那段代码其实还有如下几个变种:
[Util cameraAuth:^{
//干点啥
} fail:^{
}];
复制代码
好了,写到这里,50%的开发者已经躺枪了。
“没错咱们就是这么写的!”
这时候有人知道我想要说什么吗?对!万恶的产品经理又来了。
“我须要你在这个button事件里,再加上照片权限获取,位置权限获取,音频权限获取!”
而后代码就变成了:
[Util cameraAuth:^{
[Util audioAuth:^{
[Util photoAuth:^{
[Util locationAuth:^{
} fail:^{
}];
} fail:^{
}];
} fail:^{
}];
} fail:^{
}];
复制代码
就问你怕不怕?
别着急,饭一口一口吃,咱们如今先来拯救一下这50%的小伙伴。
其实很简单,你须要的是一个UIButton的子类。(什么玩意儿?裤子都脱了你就给我看这个??)
是的就是这样,一个UIbutton的子类,须要实现的方法大概以下:
-(void)sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event{
AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
if (authStatus == AVAuthorizationStatusRestricted || authStatus ==AVAuthorizationStatusDenied){
}else if(authStatus == AVAuthorizationStatusNotDetermined){
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
if(granted){
dispatch_async(dispatch_get_main_queue(), ^{
[super sendAction:action to:target forEvent:event];
});
}
}];
}else{
[super sendAction:action to:target forEvent:event];
}
}
复制代码
这样的话,你就能够肆无忌惮的和原生的UIbutton同样去使用它了,并不须要在本来的action中添加任何的代码。
固然有必要的话还应该对event作一下区分,以避免影响到这个button的其余功能交互。
有没有人这么作?我想确定有,并且不算少,初步估计应该有个10%左右吧。
若是你真的是这么作的,恭喜你掉坑了,这种写法有个弊端就是使用button子类替换很不方便,工做量大,并且下降了可读性,总感受哪里不太对。
不要紧,至少路子走对了,若是想要继续完善这个思路,下面的这个变种了解一下。
你知道安利runtime吗?
若是你对runtime的了解和使用仅限于Method Swizzling
和objc_setAssociatedObject
的话,往下看必定会有收获。
新建一个UIbutton的类别,假设以前的button子类为SKButton
,则添加方法以下:
- (void)setNeedsCameraPermission{
object_setClass(self, [SKButton class]]);
}
复制代码
是的你没有看错,只须要一句话,就能够把一个UIbutton,变成他的子类,不须要#import
,不须要改类名,屠龙宝刀点击就送,是否是很方便?
but...
你觉得完了吗?怎么可能。
上面的写法和直接替换一个UIbutton的子类同样,有一个共同的弊端,就是当工程内部使用的button控件自己就已是一个写好的轮子了,也是UIbutton的子类,那你怎么办?
将SKButton
的父类由UIbutton
改成当前子类?呸!不要脸!
这个思路对吗?固然对!
可是做为一个轮子,别人拿去以后还没使用就要先补胎,你好意思吗?
因此在runtime中,不只能够动态变动类,还能够动态建立类,你知道吗?
一个动态建立的支持获取相机权限的button的代码大概长这样:
- (void)setNeedsCameraPermission{
NSString *className = [NSString stringWithFormat:@"CameraPermission_%@",self.class];
Class kclass = objc_getClass([className UTF8String]);
if (!kclass)
{
kclass = objc_allocateClassPair([self class], [className UTF8String], 0);
}
SEL setterSelector = NSSelectorFromString(@"sendAction:to:forEvent:");
Method setterMethod = class_getInstanceMethod([self class], setterSelector);
object_setClass(self, kclass);
const char *types = method_getTypeEncoding(setterMethod);
class_addMethod(kclass, setterSelector, (IMP)camerapermission_SendAction, types);
objc_registerClassPair(kclass);
}
static void camerapermission_SendAction(id self, SEL _cmd, SEL action ,id target , UIEvent *event)
{
struct objc_super superclass = {
.receiver = self,
.super_class = class_getSuperclass(object_getClass(self))
};
void (*objc_msgSendSuperCasted)(const void *, SEL, SEL, id, UIEvent*) = (void *)objc_msgSendSuper;
AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
if (authStatus == AVAuthorizationStatusRestricted || authStatus ==AVAuthorizationStatusDenied){
}else if(authStatus == AVAuthorizationStatusNotDetermined){
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
if(granted){
dispatch_async(dispatch_get_main_queue(), ^{
objc_msgSendSuperCasted(&superclass, _cmd,action,target,event);
});
}
}];
}else{
objc_msgSendSuperCasted(&superclass, _cmd,action,target,event);
}
}
复制代码
这样就动态建立并替换了一个叫作“CameraPermission_XXXXXX”的button子类,任何一个button,只须要调用setNeedsCameraPermission
方法,就可以为button添加权限获取功能了。
可以写到这里的话,基本上就差很少了,不过真的有人有耐心把这么烂的文章看完吗?
若是你真的看到这的话,那必定是由于爱情了,你也必定发现了我彷佛漏掉了什么东西,我固然是故意的!
好了下面给你留一个做业,若是让你动态建立一个可自由组合,同时获取多个权限的button子类,你会写吗?