iOS中利用runtime 定制化修改系統控件

開發中經常會遇到某些需求無法通過UIKit控件暴露的屬性修改,比如下面的效果:

靠左顯示的placeholder

iOS中UISearchBar的placeholder默認是居中的,當點擊后(成為第一響應者),placeholder會向左移動,并且出現閃爍光標:

默認居中顯示的placeholder

某度一下,答案千奇百怪,比如在placeholder后面增加一堆空格,或者是用TextField+Button組合實現等等...... 雖然效果可以達到,但從此留下了很多坑,而且可讀性不好,無法維護。這里就需要另辟蹊徑,回歸控件自身。

獲取某類所有屬性/方法

雖然頭文件中暴露的公開屬性和方法足夠日常使用,但這里就需要利用runtime的相關內容獲取UISearchBar的全部屬性(主要是私有屬性)和方法來進一步尋找線索:

//需要包含頭文件  #import <objc/runtime.h>

- (void)getAllProperty:(Class )class {
    unsigned int count;
    objc_property_t *properties = class_copyPropertyList(class, &count);
    for (int i = 0; i < count; i++) {
        objc_property_t property = properties[i];
        const char *cName = property_getName(property);
        NSString *name = [NSString stringWithCString:cName encoding:NSUTF8StringEncoding];
        NSLog(@"Property Name : %@",name);
    }
}

- (void)getAllFunction:(Class )class{
    unsigned int count;
    Method *methods = class_copyMethodList(class, &count);
    for (int i = 0; i < count; i++){
        Method method = methods[i];
        SEL selector = method_getName(method);
        NSString *name = NSStringFromSelector(selector);
        const char *type =  method_getTypeEncoding(method);
        NSLog(@"Function Name: %@ Type: %s",name,type);
    }
}

調用的時候傳入UISearchBar的類:

[self getAllProperty: [UISearchBar class]];
[self getAllFunction: [UISearchBar class]];

運行,查看log:

PropertyName: searchBarTextField
FunctionName: _effectiveBarTintColor Type: @16@0:8
FunctionName: setBackgroundImage:forBarPosition:barMetrics: Type: v40@0:8@16q24q32
FunctionName: setCenterPlaceholder: Type: v20@0:8B16
......
FunctionName: centerPlaceholder Type: B16@0:8
......

此處應該會有大量log出來的方法和屬性,上面只列出部分內容,下一步通過搜索 ‘placeholder’ 這個關鍵詞來縮小查找范圍,然后就可以發現倆條有價值的信息:

FunctionName:setCenterPlaceholder:  Type:v20@0:8B16
FunctionName:centerPlaceholder Type: B16@0:8

注:這里關于Type的信息如“Type B16@0:8” 描述了返回值和參數類型,這里不做研究

很像 set/get方法吧!但對于這些沒有暴露在.h文件的方法該如何調用呢?

調用方法

既然有了 ‘setCenterPlaceholder:’ 這個方法的名字,直接可以利用NSInvocation來直接調用并傳入BOOL類型參數(注意這里的方法名包含 ‘ : ’ ):

    SEL centerSelector = NSSelectorFromString(@"setCenterPlaceholder:");
    
    if ([self.searchBar respondsToSelector:centerSelector]){
        NSMethodSignature *signature = [[UISearchBar class] instanceMethodSignatureForSelector:centerSelector];
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
        [invocation setTarget:self.searchBar];
        [invocation setSelector:centerSelector];
        BOOL isCenter = NO;
        [invocation setArgument:&isCenter atIndex:2];
        [invocation invoke];
    }

或者利用KVC:

[self.searchBarsetValue:@0 forKey:@"centerPlaceholder"];

KVC雖然調用簡單,但由于少了respondsToSelector:的判斷,容易造成crash。

最后運行,效果完美。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,150評論 25 708
  • ¥開啟¥ 【iAPP實現進入界面執行逐一顯】 〖2017-08-25 15:22:14〗 《//首先開一個線程,因...
    小菜c閱讀 6,510評論 0 17
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,890評論 18 139
  • 解決方法:打開Build Settings 搜索 Apple LLVM 9.0 - Language - Obje...
    至于么_ni閱讀 1,666評論 0 1
  • 人的一生,職業也好,前途也好,你都有嘗試和轉變的可能,而生命,只有一次,不可逆轉。 在這個世界上,所有英雄式的人物...
    GPLiu閱讀 414評論 0 0