美文网首页iOS 开发iOS 每天一读JC专题
iOS中利用runtime 定制化修改系统控件

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

作者: Lucifron | 来源:发表于2016-10-08 23:25 被阅读588次

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

    最后运行,效果完美。

    相关文章

      网友评论

        本文标题:iOS中利用runtime 定制化修改系统控件

        本文链接:https://www.haomeiwen.com/subject/fycpyttx.html