分享

通过修改UIdatePicker的字体颜色来认清Runtime的真相

 wintelsui 2015-12-21

                     本篇文章主要介绍了"通过修改UIdatePicker的字体颜色来认清Runtime的真相",主要涉及到方面的内容,对于IOS开发感兴趣的同学可以参考一下: 用过苹果原生UIdatePicker的Ios开发者都知道,UIdatePicker这个时间选择器的字体颜色默认的是黑色,并且官方API并没有提供可以修改字体颜色...

  • 用过苹果原生UIdatePicker的Ios开发者都知道,UIdatePicker这个时间选择器的字体颜色默认的是黑色,并且官方API并没有提供可以修改字体颜色的API。如下:
UIDatePicker *picker = [[UIDatePicker alloc] init];
picker.datePickerMode = UIDatePickerModeDate;
picker.maximumDate = [NSDate dateWithTimeIntervalSince1970:0];
picker.minimumDate = [NSDate dateWithTimeIntervalSinceNow:10000];
[picker setDate:[NSDate date]];

通过runtime处理之后的结果

做到上面这个效果,我主要进行了如下步骤:

  1. 利用通过遍历UIDatePicker类的所有属性,包括写在.m文件中看不到的属性。
  2. 找到苹果用于修改字体颜色的属性使用rumtime动态修改。
unsignedint outCount;
int i;
objc_property_t *pProperty = class_copyPropertyList([UIDatePicker class], &outCount);
for (i = outCount -1; i >= 0; i--)
{
// 循环获取属性的名字   property_getName函数返回一个属性的名称NSString *getPropertyName = [NSString stringWithCString:property_getName(pProperty[i]) encoding:NSUTF8StringEncoding];
NSString *getPropertyNameString = [NSString stringWithCString:property_getAttributes(pProperty[i]) encoding:NSUTF8StringEncoding];
if([getPropertyName isEqualToString:@"textColor"])
{
[picker setValue:[UIColor whiteColor] forKey:@"textColor"];
}
NSLog(@"%@====%@",getPropertyNameString,getPropertyName);
}

以上代码中pProperty返回的是一个所有属性的数组,通过循环遍历我发现@”textColor”这个属性应该就是我想要的属性,然后通过setValue赋值白色,最后就做到了上图的结果。

或许说干过两年ios开发的都知道这个东西,但是笔者更想表达的还是这种分析问题和处理问题并最终解决问题的思想,当我们修改系统原生控件遇到瓶颈的时候可以采用这种方法来学习这个控件更多的特性

如果你觉得不过瘾?那就继续吧,更刺激的还在后面呢~

  • 本来这个地方做好之后,结果测试测出一个非常奇葩的bug,就是UIDatepicker在当天的字体仍然是黑色。
    -如下图
    看见了么
    看到了么,因为我在写这篇文章的时候是九月一号,所以九月一号的字体还是以前的黑色,比如说我动态改变系统日期为九月二号,它也会自动变成九月二号的字体是黑色,由于UI设计的是背景黑色,所以这里根本就看不清,严重影响产品体验。

  • 通过一系列的纠结,我最后发现,如果UIDatepicker设置了最大时间(maxDate)和最小时间(minDate)就会这样,如果不设置就不出现这种情况,但是需求要限制最大时间和最小时间。
    在这个解决的过程中,我试过了遍历属性还有查看官方API都找不到任何资料,甚至都想不通苹果内部是怎么处理的这么牛逼的。
    实在没办法,我到处百度还是没进展,最后我终于在国外的网站找老外解决了这个问题(老外为啥比国人要牛逼呢?)
    解决这个问题的核心代码如下:

SEL selector = NSSelectorFromString(@"setHighlightsToday:");
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[UIDatePicker instanceMethodSignatureForSelector:selector]];
BOOL no = NO;
[invocation setSelector:selector];
[invocation setArgument:&no atIndex:2];
[invocation invokeWithTarget:picker];

通过这个代码我不断的逆向推理其实现原理,并且各种百度查找NSInvocation的相关资料。
最后让我逐步来解析一下每一行代码的意思:

  1. SEL selector =NSSelectorFromString(@”setHighlightsToday:”);
    通过Runtime的NSSelectorFromString利用字符串获取方法@”setHighlightsToday:”(注意,看这个方法名就是今天的高亮显示开关,注意字符串后面的冒号,表示这个方法是有参数的)

  2. NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[UIDatePicker instanceMethodSignatureForSelector:selector]];
    用这个SEL创建UIdatepicker类的NSMethodSignature来创建一个NSInvocation。

  3. BOOL no = NO;
    [invocation setSelector:selector];
    [invocation setArgument:&no atIndex:2];
    [invocation invokeWithTarget:picker];
    给invocation设置参数,设置target,并且动态调用设置参数NO(苹果默认YES并且不开放)
    其实NSInvocation也是Runtime的一种方式。
    注意[invocation setArgument:&no atIndex:2];这个2是怎么来的,因为setArgument中第一个参数的类picker,第二个参数是SEL,所以我们自己的参数最小的数字就是2,也就是这个方法的第一个参数

最后问题彻底解决了,晒张截图求真相:

虽然问题是最终解决了,但是假如下次我们又遇到这种类似的问题应该怎么办呢?难道又要焦头烂额的到处寻求援助么?程序员的上进心和求学心哪里去了?

最后笔者花了一到两天的时间一直思考这个问题,并最终通过分析老外的解决方式逆向找到解决的具体原理

前面说过,可以通过NSString *getPropertyName = [NSString stringWithCString:property_getName(pProperty[i]) encoding:NSUTF8StringEncoding];来获取一个类所有的属性,最后我在Runtime的API中发现,也可以获取一个类所有的方法(有兴趣的可以去了解一下IMP指针)。

unsigned int outCount;
int i;
Method *method = class_copyMethodList([UIDatePicker class],&outCount);

也就是说,利用Rumtime遍历某个类的所有属性和所有的方法,苹果的API限制在我们面前形同虚设,可以解决更多的一些原生API不开放的东西,其实这里也可以自己重写,不过相当麻烦,而且会有复杂的内存问题,所以还是直接用Runtime绕过苹果限制修改原生控件简单的多

其实runtime本身的东西很少,更多的是一些概念性的东西,有兴趣的也可以去了解一下runtime,本文主要讲的是一种利用runtime去解决实际问题的思维方式

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多