iOS土味儿讲义(一)--一个Button引起的血案

开篇作一下更新说明,当你看完这篇文章的时候,若是你以为文章里面的实现方案与需求不是那么合拍,请不要怀疑本身,由于我也这么以为。但其实主要目的仍是为了介绍一下runtime中一个不太经常使用的知识点,实现需求只是顺带,感谢盒子大佬的讲解,学到了不少东西。bash

大地母亲在忽悠护佑着你

iOS开发作了好几年,一直想写点东西却没有动手,一个是由于懒,还有一个就是我一个同事说的,我写啥?想写的别人都写过了。网络

事实上也确实是这样,不管你想知道什么知识点,几乎都能在网上找到答案,实在是没那个必要在后面跟风,关键是你尚未人家讲得好。async

可是凡事也有利弊,资料太多了不免选择恐惧症,随便搜一个组件化流程都能找到十几个不一样版本的方案,最可怕的是我还以为他们说的都对!这就很尴尬了。ide

更可悲的是,看了这么多的组件化教程,被安利了各类库以后,我依然没有把组件化学会。组件化

就比如听过了许多大道理,却依旧过很差这一辈子。记住了许多理论,却依然写很差代码。ui

这也是土系魔法讲义的由来,这个系列的每一篇文章都会更接地气一些,以一个具体需求为起始,用一种特殊的方式将文章的中心点讲述出来。spa

文章会以code和思路为主,讲解部分比较少,根据需求随时变动。线程

若是有什么地方说的不对,不用过来打我,我确定改。code

从一个Button说开去

一个最基本的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方法里面。

固然这个写法实在是太...,除了萌新以外不多真的有人这么写了,可是其实上面那段代码其实还有如下几个变种:

  1. 知道将button本来的响应事件单独提出来,至少不用写两遍。
  2. 将获取权限的代码封装起来,大概这样:
    [Util cameraAuth:^{
       //干点啥    
    } fail:^{
        
    }];
    复制代码
  3. 以上两种方法互相结合。

好了,写到这里,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 Swizzlingobjc_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子类,你会写吗?

相关文章
相关标签/搜索