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子類,你會寫嗎?

相關文章
相關標籤/搜索