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,455评论 25 708
  • ¥开启¥ 【iAPP实现进入界面执行逐一显】 〖2017-08-25 15:22:14〗 《//首先开一个线程,因...
    小菜c阅读 6,537评论 0 17
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,973评论 19 139
  • 解决方法:打开Build Settings 搜索 Apple LLVM 9.0 - Language - Obje...
    至于么_ni阅读 1,666评论 0 1
  • 人的一生,职业也好,前途也好,你都有尝试和转变的可能,而生命,只有一次,不可逆转。 在这个世界上,所有英雄式的人物...
    GPLiu阅读 416评论 0 0