web-dev-qa-db-fra.com

Personnalisez la couleur du texte d'UIDatePicker pour iOS7 (comme le fait Mailbox)

J'ai le dilemme le plus frustrant. J'ai fait des recherches de haut en bas et je peux clairement voir que Apple ne veut pas que nous trafiquions avec iOS 7. Eh bien, je veux falsifier. Et, l'équipe de Mailbox a clairement compris comment faire et obtenir approuvé.

La principale chose que j'essaie de réaliser est de changer la couleur de l'étiquette en blanc.

Mailbox UIDatePicker

Ma première pensée a été qu'ils utilisent un UIPickerView personnalisé qui imite simplement un UIDatePicker, mais je ne pense pas que ce soit le cas.

J'ai zoomé sur un petit fragment et découvert des restes d'un UIDatePicker normal (lignes noires) avec un écrêtage sur la lettre "W".

Mailbox UIDatePicker fragments

Maintenant, j'ai parcouru haut et bas. A fait du piratage d'exécution, a dérangé avec UIAppearance, et a même creusé dans certaines API privées juste pour voir si cela est possible.

Je me suis rapproché, très proche, mais il a utilisé une API privée, et si vous faites défiler assez vite, les étiquettes redeviendront noires.

Je ne sais absolument pas comment procéder sans a) enfreindre les règles ou b) passer d'innombrables heures à réimplémenter UIDatePicker.

Boîte aux lettres, dites-moi vos secrets! Et si quelqu'un d'autre a des suggestions (et je veux dire toutes), faites-le moi savoir.

Aussi, c'est le plus proche que j'ai obtenu:

So close yet so far

36
rnystrom

J'ai besoin de similaires pour mon application et j'ai fini par faire le long chemin. C'est vraiment dommage qu'il n'y ait pas de moyen plus simple de passer simplement à une version en texte blanc d'UIDatePicker.

Le code ci-dessous utilise une catégorie sur UILabel pour forcer la couleur du texte de l'étiquette à être blanche lorsque le message setTextColor: est envoyé à l'étiquette. Afin de ne pas le faire pour chaque étiquette de l'application, je l'ai filtré pour ne s'appliquer que s'il s'agit d'une sous-vue d'une classe UIDatePicker. Enfin, certaines étiquettes ont leurs couleurs définies avant d'être ajoutées à leurs super-aperçus. Pour les intercepter, le code remplace la méthode willMoveToSuperview :.

Il serait probablement préférable de diviser les éléments ci-dessous en plusieurs catégories, mais je les ai tous ajoutés ici pour faciliter la publication.

#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
59
Sig

Voici un bon extrait de travail. Testé sur iOS 7.1, 8. et 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];

J'ai ajouté une condition simple pour interrompre le processus de compilation si vous construisez pour iOS> 8.1, car je ne peux pas être sûr que ce hack fonctionnera, et vous ne voulez pas avoir tout plantage en production à cause de cela.

Le setHighlightsToday: le sélecteur est utilisé car UIDatePicker utilise [UIColor blackColor] par défaut pour afficher la date actuelle.

30
arturgrigor

J'ai trouvé l'astuce qui définissait la couleur de la police sur n'importe quelle couleur et ne me trompais pas avec l'étiquette Today.

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

Voici le résultat:

Et tous les crédits de cette solution vont à cet article dans un autre thread: https://stackoverflow.com/a/33459744/1236334

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

Cela a fonctionné pour moi.

10
fnc12

Le moyen infaillible de le faire consiste à parcourir les sous-vues et les sous-vues des sous-vues jusqu'à ce que vous trouviez la classe que vous recherchez (dans ce cas, une sorte d'UILabel) et définissez manuellement les propriétés.

Pour trouver celle que vous recherchez, je vous suggère d'utiliser une application comme X-ray ou Reveal pour inspecter la hiérarchie des vues afin d'obtenir le nom exact de la classe, puis:


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

C'est une façon très fragile de faire les choses, mais elle n'appelle techniquement jamais une API privée, et cela ne ferait pas peur à Apple. De retour dans iOS 5, je faisais des choses comme ça pour agrandir UIAlertViews sur les iPads pour avoir plus de texte sans la vue de défilement intégrée.

Beaucoup d'essais et d'erreurs peuvent être nécessaires, et cela peut ne pas être joli, mais cela fonctionnera.

(Comme un gars très intelligent que je connais a dit: "tout le code sera perdu pour le compilateur, comme des larmes sous la pluie.")

3
Swizzlr