開發中經常會遇到某些需求無法通過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。
最后運行,效果完美。