我能改变iOS7中日期选择器的字体颜色吗?

36

我刚刚下载了Xcode 5,想知道如何更改日期选择器中的字体颜色或大小,有没有人知道?


我面临着同样的问题...你是如何解决UIDatePicker而不是UIPicker的? - Fahim Parkar
可能是使用UIAppearance自定义iOS UIDatepicker的重复问题。 - Saqib Saud
可能是https://dev59.com/lmIk5IYBdhLWcg3we-L5的重复问题。 - Gajendra K Chauhan
颜色很容易:https://dev59.com/u2Ei5IYBdhLWcg3w4PdB#31775498 - Thomás Pereira
11个回答

59
我需要与我的应用程序类似的功能,并且最终选择了较长的方式。很遗憾,没有更简单的方法来切换到 UIDatePicker 的白色文本版本。
下面的代码使用 UILabel 的分类,强制标签的文本颜色为白色,当发送 setTextColor: 消息给该标签时。为了不在应用程序中的每个标签都执行此操作,我已经过滤掉仅适用于 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 label 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

4
这个解决方案比"自己编写"的方案更好,因为它保留了本地设置。 - Jon
2
仅仅因为使用了Swizzling,就加1分。我想从技术上讲,这甚至没有使用任何私有API。 - c roald
17
这个方案能通过App Store的审核吗?有人试过吗? - Stas Zhukovskiy
感谢您为解决这个问题所做的努力。willMoveToSuperview 方法的使用非常不错。我还添加了交换方法来更改标签的字体和大小写。 - Seamus
1
这个解决方案会通过App Store的审查吗?有人试过吗? - jayellos
显示剩余6条评论

41

这是最简单的方法,它也非常有效!我保证。在视图中创建UIDatePicker,或通过storyboard连接它,然后将以下代码放入ViewDidLoad方法中。请用您的日期选择器名称替换第一行中的 "datepicker"。

Objective-C:

//Set Color of Date Picker
self.datePicker.datePickerMode = UIDatePickerModeDate;
[self.datePicker setValue:[UIColor colorWithRed:70/255.0f green:161/255.0f blue:174/255.0f alpha:1.0f] 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];

我在这上面浪费了很多时间,而这是唯一一个适用于UIDatePicker实例的方法。 - Brian Donovan
如何在UIDatePicker中更改字体? - Gaurav
@TriannaBrannon 看起来在 Swift 中已经没有 NSInvocation 了: https://developer.apple.com/swift/blog/?id=19,因此无法编译。 - Juan Boero
1
Õ£¿Swiftõ©¡datePicker.setValue(UIColor.whiteColor(), forKey: "textColor") datePicker.setValue(false, forKey: "highlightsToday") - Maciej Trybiło

26

我使用 UIControlUIPicker 实现了一个用于 iOS 7 的 UIDatePicker 替代品。这使您可以编辑所有您想要的内容,包括文本字体和颜色。

唯一无法更改的是选定行周围线条的颜色。

有些地方可以进一步简化,它仅显示日期,不包括时间。这可以作为任何想要做类似事情的人的起点。

enter image description here

@interface SBDatePicker : UIControl

@property (nonatomic, strong, readwrite) NSDate *minimumDate;
@property (nonatomic, strong, readwrite) NSDate *maximumDate;
@property (nonatomic, strong, readwrite) NSDate *date;

- (void)setDate:(NSDate *)date animated:(BOOL)animated;

@end


#import "SBDatePicker.h"

const NSUInteger NUM_COMPONENTS = 3;

typedef enum {
    kSBDatePickerInvalid = 0,
    kSBDatePickerYear,
    kSBDatePickerMonth,
    kSBDatePickerDay
} SBDatePickerComponent;

@interface SBDatePicker () <UIPickerViewDataSource, UIPickerViewDelegate> {
    SBDatePickerComponent _components[NUM_COMPONENTS];
}

@property (nonatomic, strong, readwrite) NSCalendar *calendar;
@property (nonatomic, weak, readwrite) UIPickerView *picker;

@property (nonatomic, strong, readwrite) NSDateFormatter *dateFormatter;

@property (nonatomic, strong, readwrite) NSDateComponents *currentDateComponents;

@property (nonatomic, strong, readwrite) UIFont *font;

@end

@implementation SBDatePicker

#pragma mark - Life cycle

- (id)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];

    if (!self) {
        return nil;
    }

    [self commonInit];

    return self;
}

- (id)initWithCoder:(NSCoder *)coder {
    self = [super initWithCoder:coder];

    if (!self) {
        return nil;
    }

    [self commonInit];

    return self;
}

- (void)commonInit {
    self.tintColor = [UIColor whiteColor];
    self.font = [UIFont systemFontOfSize:23.0f];

    self.calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
    [self setLocale:[NSLocale currentLocale]];

    UIPickerView *picker = [[UIPickerView alloc] initWithFrame:self.bounds];
    picker.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
    picker.dataSource = self;
    picker.delegate = self;
    picker.tintColor = [UIColor whiteColor];

    self.date = [NSDate date];

    [self addSubview:picker];
    self.picker = picker;
}

- (CGSize)intrinsicContentSize {
    return CGSizeMake(320.0f, 216.0f);
}

#pragma mark - Setup

- (void)setMinimumDate:(NSDate *)minimumDate {
    _minimumDate = minimumDate;

    [self updateComponents];
}

- (void)setMaximumDate:(NSDate *)maximumDate {
    _maximumDate = maximumDate;

    [self updateComponents];
}

- (void)setDate:(NSDate *)date {
    [self setDate:date animated:NO];
}

- (void)setDate:(NSDate *)date animated:(BOOL)animated {
    self.currentDateComponents = [self.calendar components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit)
                                                  fromDate:date];

    [self.picker reloadAllComponents];
    [self setIndicesAnimated:YES];
}

- (NSDate *)date {
    return [self.calendar dateFromComponents:self.currentDateComponents];
}

- (void)setLocale:(NSLocale *)locale {
    self.calendar.locale = locale;

    [self updateComponents];
}

- (SBDatePickerComponent)componentFromLetter:(NSString *)letter {
    if ([letter isEqualToString:@"y"]) {
        return kSBDatePickerYear;
    }
    else if ([letter isEqualToString:@"m"]) {
        return kSBDatePickerMonth;
    }
    else if ([letter isEqualToString:@"d"]) {
        return kSBDatePickerDay;
    }
    else {
        return kSBDatePickerInvalid;
    }
}

- (SBDatePickerComponent)thirdComponentFromFirstComponent:(SBDatePickerComponent)component1
                                       andSecondComponent:(SBDatePickerComponent)component2 {

    NSMutableIndexSet *set = [NSMutableIndexSet indexSetWithIndexesInRange:NSMakeRange(kSBDatePickerInvalid + 1, NUM_COMPONENTS)];
    [set removeIndex:component1];
    [set removeIndex:component2];

    return (SBDatePickerComponent) [set firstIndex];
}

- (void)updateComponents {
    NSString *componentsOrdering = [NSDateFormatter dateFormatFromTemplate:@"yMMMMd" options:0 locale:self.calendar.locale];
    componentsOrdering = [componentsOrdering lowercaseString];

    NSString *firstLetter = [componentsOrdering substringToIndex:1];
    NSString *lastLetter = [componentsOrdering substringFromIndex:(componentsOrdering.length - 1)];

    _components[0] = [self componentFromLetter:firstLetter];
    _components[2] = [self componentFromLetter:lastLetter];
    _components[1] = [self thirdComponentFromFirstComponent:_components[0] andSecondComponent:_components[2]];

    self.dateFormatter = [[NSDateFormatter alloc] init];
    self.dateFormatter.calendar = self.calendar;
    self.dateFormatter.locale = self.calendar.locale;

    [self.picker reloadAllComponents];

    [self setIndicesAnimated:NO];
}

- (void)setIndexForComponentIndex:(NSUInteger)componentIndex animated:(BOOL)animated {
    SBDatePickerComponent component = [self componentForIndex:componentIndex];
    NSRange unitRange = [self rangeForComponent:component];

    NSInteger value;

    if (component == kSBDatePickerYear) {
        value = self.currentDateComponents.year;
    }
    else if (component == kSBDatePickerMonth) {
        value = self.currentDateComponents.month;
    }
    else if (component == kSBDatePickerDay) {
        value = self.currentDateComponents.day;
    }
    else {
        assert(NO);
    }

    NSInteger index = (value - unitRange.location);
    NSInteger middleIndex = (INT16_MAX / 2) - (INT16_MAX / 2) % unitRange.length + index;

    [self.picker selectRow:middleIndex inComponent:componentIndex animated:animated];
}

- (void)setIndicesAnimated:(BOOL)animated {
    for (NSUInteger componentIndex = 0; componentIndex < NUM_COMPONENTS; componentIndex++) {
        [self setIndexForComponentIndex:componentIndex animated:animated];
    }
}

- (SBDatePickerComponent)componentForIndex:(NSInteger)componentIndex {
    return _components[componentIndex];
}

- (NSCalendarUnit)unitForComponent:(SBDatePickerComponent)component {
    if (component == kSBDatePickerYear) {
        return NSYearCalendarUnit;
    }
    else if (component == kSBDatePickerMonth) {
        return NSMonthCalendarUnit;
    }
    else if (component == kSBDatePickerDay) {
        return NSDayCalendarUnit;
    }
    else {
        assert(NO);
    }
}

- (NSRange)rangeForComponent:(SBDatePickerComponent)component {
    NSCalendarUnit unit = [self unitForComponent:component];

    return [self.calendar maximumRangeOfUnit:unit];
}

#pragma mark - Data source

- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
    return 3;
}

- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)componentIndex {
    return INT16_MAX;
}

#pragma mark - Delegate

- (CGFloat)pickerView:(UIPickerView *)pickerView widthForComponent:(NSInteger)componentIndex {
    SBDatePickerComponent component = [self componentForIndex:componentIndex];

    if (component == kSBDatePickerYear) {
        CGSize size = [@"0000" sizeWithAttributes:@{NSFontAttributeName : self.font}];

        return size.width + 25.0f;
    }
    else if (component == kSBDatePickerMonth) {
        CGFloat maxWidth = 0.0f;

        for (NSString *monthName in self.dateFormatter.monthSymbols) {
            CGFloat monthWidth = [monthName sizeWithAttributes:@{NSFontAttributeName : self.font}].width;

            maxWidth = MAX(monthWidth, maxWidth);
        }

        return maxWidth + 25.0f;
    }
    else if (component == kSBDatePickerDay) {
        CGSize size = [@"00" sizeWithAttributes:@{NSFontAttributeName : self.font}];

        return size.width + 25.0f;
    }
    else {
        return 0.01f;
    }
}

- (NSString *)titleForRow:(NSInteger)row forComponent:(SBDatePickerComponent)component {
    NSRange unitRange = [self rangeForComponent:component];
    NSInteger value = unitRange.location + (row % unitRange.length);

    if (component == kSBDatePickerYear) {
        return [NSString stringWithFormat:@"%li", (long) value];
    }
    else if (component == kSBDatePickerMonth) {
        return [self.dateFormatter.monthSymbols objectAtIndex:(value - 1)];
    }
    else if (component == kSBDatePickerDay) {
        return [NSString stringWithFormat:@"%li", (long) value];
    }
    else {
        return @"";
    }
}

- (NSInteger)valueForRow:(NSInteger)row andComponent:(SBDatePickerComponent)component {
    NSRange unitRange = [self rangeForComponent:component];

    return (row % unitRange.length) + unitRange.location;
}

- (BOOL)isEnabledRow:(NSInteger)row forComponent:(NSInteger)componentIndex {
    NSDateComponents *dateComponents = [[NSDateComponents alloc] init];
    dateComponents.year = self.currentDateComponents.year;
    dateComponents.month = self.currentDateComponents.month;
    dateComponents.day = self.currentDateComponents.day;

    SBDatePickerComponent component = [self componentForIndex:componentIndex];
    NSInteger value = [self valueForRow:row andComponent:component];

    if (component == kSBDatePickerYear) {
        dateComponents.year = value;
    }
    else if (component == kSBDatePickerMonth) {
        dateComponents.month = value;
    }
    else if (component == kSBDatePickerDay) {
        dateComponents.day = value;
    }

    NSDate *rowDate = [self.calendar dateFromComponents:dateComponents];

    if (self.minimumDate != nil && [self.minimumDate compare:rowDate] == NSOrderedDescending) {
        return NO;
    }
    else if (self.maximumDate != nil && [rowDate compare:self.maximumDate] == NSOrderedDescending) {
        return NO;
    }

    return YES;
}

- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)componentIndex reusingView:(UIView *)view {
    UILabel *label;

    if ([view isKindOfClass:[UILabel class]]) {
        label = (UILabel *) view;
    }
    else {
        label = [[UILabel alloc] init];
        label.font = self.font;
    }

    SBDatePickerComponent component = [self componentForIndex:componentIndex];
    NSString *title = [self titleForRow:row forComponent:component];

    UIColor *color;

    BOOL enabled = [self isEnabledRow:row forComponent:componentIndex];

    if (enabled) {
        color = [UIColor whiteColor];
    }
    else {
        color = [UIColor colorWithWhite:0.0f alpha:0.5f];
    }

    NSAttributedString *attributedTitle = [[NSAttributedString alloc] initWithString:title
attributes:@{NSForegroundColorAttributeName: color}];

    label.attributedText = attributedTitle;

    if (component == kSBDatePickerMonth) {
        label.textAlignment = NSTextAlignmentLeft;
    }
    else {
        label.textAlignment = NSTextAlignmentRight;
    }

    return label;
}

- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)componentIndex {
    SBDatePickerComponent component = [self componentForIndex:componentIndex];
    NSInteger value = [self valueForRow:row andComponent:component];

    if (component == kSBDatePickerYear) {
        self.currentDateComponents.year = value;
    }
    else if (component == kSBDatePickerMonth) {
        self.currentDateComponents.month = value;
    }
    else if (component == kSBDatePickerDay) {
        self.currentDateComponents.day = value;
    }
    else {
        assert(NO);
    }

    [self setIndexForComponentIndex:componentIndex animated:NO];

    NSDate *datePicked = self.date;

    if (self.minimumDate != nil && [datePicked compare:self.minimumDate] == NSOrderedAscending) {
        [self setDate:self.minimumDate animated:YES];
    }
    else if (self.maximumDate != nil && [datePicked compare:self.maximumDate] == NSOrderedDescending) {
        [self setDate:self.maximumDate animated:YES];
    }
    else {
        [self.picker reloadAllComponents];
    }

    [self sendActionsForControlEvents:UIControlEventValueChanged];
}

@end

你也有 #import "NSMutableAttributedString+Append.h" 的代码吗? - James Laurenstin
哦,那只是我用来创建属性字符串的简单类别。这里不需要它。我会从源代码中删除它。 - Sulthan
好的,算了吧...我通过一些微小的更改解决了它。谢谢。它可以工作了。 - James Laurenstin
日期验证怎么样,比如二月只有28天? - Gema Megantara
@GemaMegantara 它在哪里丢失了? - Sulthan
显示剩余7条评论

10

在viewDidLoad中,

datePicker.setValue(UIColor.yellowColor(), forKey: "textColor")
datePicker.performSelector("setHighlightsToday:", withObject:UIColor.yellowColor())

适用于iOS 9


1
顶部的那个可以工作,底部的那个不行。Xcode 7.3.1,iOS 9.3。 - Bryan Bryce
底部的代码会抛出异常。 - Nike Kov

10

我也遇到了同样的问题,黑色背景上的黑色文本。 :) 目前我还没有找到改变文字颜色的方法。 不过你可以尝试调整背景颜色:

picker.backgroundColor = [UIColor whiteColor];

我发现的另一个有效的方法是为datepicker中的每个UIView(每个数字都有自己的UIView,是的,我知道)

[UIView appearanceWhenContainedIn:[UITableView class], [UIDatePicker class], nil].backgroundColor = [UIColor colorWithWhite:1 alpha:0.5]

(我不建议使用这个选项,因为旋转时会产生令人烦恼的闪烁效果。)

希望这可以帮到你。:)


6

虽然有点小技巧,但其他解决方案也是如此。以下方法适用于我:子类化UIDatePicker并在添加任何视图之前更改textColor属性的值。

如果只想更改颜色:

class ColoredDatePicker: UIDatePicker {
    var changed = false
    override func addSubview(view: UIView) {
        if !changed {
            changed = true
            self.setValue(yourColor, forKey: "textColor")
        }
        super.addSubview(view)
    }
}

改变颜色和其他属性:

如果我想到了,我会更新的。

我注意到颜色并不完全符合颜色对象的规定,但是可以尝试调整它,使其成为你想要的颜色。


1
我想要的只是白色,这对我来说完美地解决了问题。谢谢! - joslinm

4

我在这里回答了一个类似的问题:UIPickerView Custom Fonts iOS7。基本上,您需要修改viewForRow:方法,目前我只能让它在UIPickerView上运行,而不是UIDatePicker。(正在努力中...)

-(UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view

以下是如何为UIPickerView设置属性文本(包括自定义字体、颜色等)的方法:

-(UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view
{
// create attributed string
NSString *yourString;  //can also use array[row] to get string
NSDictionary *attributeDict = @{NSForegroundColorAttributeName : [UIColor whiteColor]};
NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:yourString attributes:attributeDict];

// add the string to a label's attributedText property
UILabel *labelView = [[UILabel alloc] init];
labelView.attributedText = attributedString;

// return the label
return labelView;

希望这能帮到你... 我一找出日期选择器就会编辑我的回答!


4
这件事情有进展了吗? - bmueller

3

这适用于iOS 9。

[datePicker setValue:[UIColor whiteColor] forKey:@"textColor"];

2
它不能直接工作... 只有在滚动选择器后才能工作。 - Mehul Thakkar

2
在Swift中,您可以在viewDidLoad中添加以下两行代码:
    datePicker.setValue(DesignHelper.getOffWhiteColor(), forKey: "textColor")
    datePicker.performSelector("setHighlightsToday:", withObject: UIColor.whiteColor())

设计助手从哪里来的? - lborgav
糟糕,忽略那个。这是一个自定义类,它返回一个UIColor。 - Jeremiah
我尝试使用UIColor.redColor(),但它没有起作用。 - lborgav

0
我偶然发现了一个非常简洁的解决方案,使用了UIAppearance。我发现尝试通过UIAppearanceUIPickerView中的任何UILabel设置textColor没有效果,但是一个自定义的外观属性,只需调用常规的textColor setter就可以正常工作。
// Implement a custom appearance property via a UILabel category
@interface UILabel (PickerLabelTextColor)

@property (nonatomic, strong) UIColor * textColorWorkaround UI_APPEARANCE_SELECTOR;

@end

@implementation UILabel (PickerLabelTextColor)

- (UIColor *)textColorWorkaround {
    return self.textColor;
}

- (void)setTextColorWorkaround:(UIColor *)textColor {
    self.textColor = textColor;
}

@end

然后按如下方式使用:

UILabel *pickerLabelProxy = [UILabel appearanceWhenContainedInInstancesOfClasses:@[UIPickerView.class]];
pickerLabelProxy.textColorWorkaround = UIColor.lightGrayColor;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接