iOS中利用runtime 定制化修改系统控件
2016-10-08 本文已影响588人
Lucifron
开发中经常会遇到某些需求无法通过UIKit控件暴露的属性修改,比如下面的效果:
靠左显示的placeholderiOS中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。
最后运行,效果完美。