iOS 开发iOS 每天一读JC专题

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

2016-10-08  本文已影响588人  Lucifron

开发中经常会遇到某些需求无法通过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。

最后运行,效果完美。

上一篇下一篇

猜你喜欢

热点阅读