自定义UIDatePicker for iOS7的文本颜色(就像Mailbox一样)

时间:2013-11-24 21:26:12

标签: ios uikit uipickerview uidatepicker uiappearance

我遇到了最令人沮丧的困境。我上下研究过,可以清楚地看到Apple不希望我们篡改iOS 7.好吧,我想篡改。而且,Mailbox的团队清楚地知道如何做到并获得批准。

我想要实现的主要目的是将标签颜色更改为白色。

Mailbox UIDatePicker

我的第一个想法是他们使用的是一个模仿UIDatePicker的自定义UIPickerView,但我不认为是这种情况。

我放大了一个小片段,发现了正常UIDatePicker的残余部分(黑色线条)以及字母“W”上的剪辑。

Mailbox UIDatePicker fragments

现在我已经高高低调了。做了一些运行时黑客攻击,与UIAppearance混淆,甚至挖到一些私有API只是为了看看这是否可行。

我接近,非常接近,但是它使用了私有API,如果滚动得足够快,标签会再次变黑。

如果没有a)违反规则或b)花费无数小时重新实现UIDatePicker,我完全不知道如何做到这一点。

邮箱,告诉我你的秘密!如果其他人有任何建议(我的意思是任何建议),请告诉我。

此外,这是我最接近的:

So close yet so far

5 个答案:

答案 0 :(得分:59)

我的应用程序需要类似的,并且最终走了很长一段路。简单地切换到UIDatePicker的白色文本版本并不是一个真正的耻辱。

当setTextColor:消息发送到标签时,下面的代码使用UILabel上的类别强制标签的文本颜色为白色。为了不对应用中的每个标签执行此操作,我将其过滤为仅在UIDatePicker类的子视图中应用。最后,一些标签在添加到超级视图之前已经设置了颜色。要捕获这些代码,请覆盖willMoveToSuperview:方法。

将下面的内容拆分为多个类别可能会更好,但为了便于发布,我在这里添加了所有内容。

#import "UILabel+WhiteUIDatePickerLabels.h"
#import <objc/runtime.h>

@implementation UILabel (WhiteUIDatePickerLabels)

+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        [self swizzleInstanceSelector:@selector(setTextColor:)
                      withNewSelector:@selector(swizzledSetTextColor:)];
        [self swizzleInstanceSelector:@selector(willMoveToSuperview::)
                      withNewSelector:@selector(swizzledWillMoveToSuperview:)];
    });
}

// Forces the text colour of the lable to be white only for UIDatePicker and its components
-(void) swizzledSetTextColor:(UIColor *)textColor {
    if([self view:self hasSuperviewOfClass:[UIDatePicker class]] ||
       [self view:self hasSuperviewOfClass:NSClassFromString(@"UIDatePickerWeekMonthDayView")] ||
       [self view:self hasSuperviewOfClass:NSClassFromString(@"UIDatePickerContentView")]){
        [self swizzledSetTextColor:[UIColor whiteColor]];
    } else {
        //Carry on with the default
        [self swizzledSetTextColor:textColor];
    }
}

// Some of the UILabels haven't been added to a superview yet so listen for when they do.
- (void) swizzledWillMoveToSuperview:(UIView *)newSuperview {
    [self swizzledSetTextColor:self.textColor];
    [self swizzledWillMoveToSuperview:newSuperview];
}

// -- helpers --
- (BOOL) view:(UIView *) view hasSuperviewOfClass:(Class) class {
    if(view.superview){
        if ([view.superview isKindOfClass:class]){
            return true;
        }
        return [self view:view.superview hasSuperviewOfClass:class];
    }
    return false;
}

+ (void) swizzleInstanceSelector:(SEL)originalSelector
                 withNewSelector:(SEL)newSelector
{
    Method originalMethod = class_getInstanceMethod(self, originalSelector);
    Method newMethod = class_getInstanceMethod(self, newSelector);

    BOOL methodAdded = class_addMethod([self class],
                                       originalSelector,
                                       method_getImplementation(newMethod),
                                       method_getTypeEncoding(newMethod));

    if (methodAdded) {
        class_replaceMethod([self class],
                            newSelector,
                            method_getImplementation(originalMethod),
                            method_getTypeEncoding(originalMethod));
    } else {
        method_exchangeImplementations(originalMethod, newMethod);
    }
}

@end

答案 1 :(得分:30)

这是一个很好的工作片段。测试 iOS 7.1 8.0 8.1

#if __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_8_1
    #error "Check if this hack works on this OS"
#endif

    [self.datePicker setValue:[UIColor whiteColor] forKeyPath:@"textColor"];

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

如果您正在为iOS&gt;构建,我添加了一个简单的条件来打破编译过程。 8.1 ,因为我无法确定此黑客是否有效,并且您不希望因此而在生产中出现任何崩溃。

使用了setHighlightsToday:选择器,因为UIDatePicker默认使用[UIColor blackColor]来显示当前日期。

答案 2 :(得分:14)

我找到了将字体颜色设置为任何颜色的技巧,也不会搞砸今日标签。

datePicker.setValue(UIColor.whiteColor(), forKeyPath: "textColor")
datePicker.datePickerMode = .CountDownTimer
datePicker.datePickerMode = .DateAndTime //or whatever your original mode was

结果如下:

此解决方案的所有学分都转到其他帖子的帖子中: https://stackoverflow.com/a/33459744/1236334

答案 3 :(得分:10)

self.birthDatePicker.setValue(UIColor.whiteColor(), forKeyPath: "textColor")

这对我有用。

答案 4 :(得分:3)

执行此操作的绝对方法是迭代子视图和子视图的子视图,直到找到您要查找的类(在本例中为某种UILabel)并手动设置其属性。

要查找您正在寻找的那个,我建议您使用像X-ray或Reveal这样的应用来检查视图层次结构以获得确切的类名,然后:


for (UIView * subview in datePicker.subviews) {
    if ([subview isKindOfClass:NSClassFromString:@"_UISomePrivateLabelSubclass"]) {
        [subview setColor:...
        //do interesting stuff here
    }
}

这是一种非常脆弱的做事方式,但从技术上讲它并不是私有API,它不会吓到Apple。回到iOS 5,我曾经做过这样的事情,让iPad上的UIAlertViews更大,以便在没有嵌入式滚动视图的情况下拥有更多文本。

可能需要进行大量的试验和错误,它可能看起来不太漂亮,但它会起作用。

(作为一个非常聪明的家伙,我知道说:“所有的代码都将丢失给编译器,就像雨中的泪水。”)