UIAppearance не влияет на UILabels, созданные программно

Мы расширили UILabel, чтобы иметь возможность применять стандартные шрифты и цвета для всех случаев использования данного типа метки в наших приложениях. Например.

@interface UILabelHeadingBold : UILabel
@end

В нашем AppDelegate мы применяем такие шрифты и цвета

[[UILabelHeadingBold appearance] setTextColor:<some color>];
[[UILabelHeadingBold appearance] setFont:<some font>];

При добавлении UILabel в наши XIB теперь мы можем выбрать класс типа UILabelHeadingBold, и он работает, как и ожидалось. Метка отображается с правильным шрифтом и цветом, как указано в нашем AppDelegate.

Однако, если мы создадим метку программно, например.

UILabelHeadingBold *headingLabel = [[UILabelHeadingBold alloc] initWithFrame:CGRectMake(10, 10, 100, 30)];
[self.mainView addSubview:headingLabel];

UILabel не применяет ожидаемый шрифт/цвет. Мы должны вручную применить эти атрибуты.

Есть ли способ заставить UIAppearance воздействовать на программно созданные элементы пользовательского интерфейса или он работает только при использовании в XIB?


person ckibsen    schedule 03.07.2012    source источник


Ответы (5)


Из документации Apple:

Для поддержки настройки внешнего вида класс должен соответствовать протоколу UIAppearanceContainer, а соответствующие методы доступа должны быть отмечены UI_APPEARANCE_SELECTOR.

Например, в UINavigationBar.h tintColor помечен как UI_APPEARANCE_SELECTOR

@property(nonatomic,retain) UIColor *tintColor UI_APPEARANCE_SELECTOR;

Но в UILabel.h вы можете видеть, что свойства textColor и font не отмечены UI_APPEARANCE_SELECTOR, но каким-то образом это работает при добавлении в Interface Builder (следуя документации, это вообще не должно работать).

person Moxy    schedule 05.08.2012
comment
Это действительно иногда работает и для UILabels, созданных в коде, но результаты противоречивы, и поведение отличается между iOS 5 и, ну, вы знаете. Так что просто не используйте UIAppearance для настройки UILabel - person Joshua J. McKinnon; 07.08.2012
comment
+1 за информацию о том, что я не единственный, кто получает противоречивые результаты (мне потребовалось несколько часов кодирования, пока я не нашел ваш комментарий). +1 за решение robert.wijas, которое я использую сейчас, и все в порядке. - person anneblue; 22.08.2013

Простой хак, который у меня работает без проблем, заключается в создании категории с помощью установщика UIAppearance, который изменяет свойства UILabel.

Следуя соглашениям UIAppearance, я создал метод:

- (void)setTextAttributes:(NSDictionary *)numberTextAttributes;
{
    UIFont *font = [numberTextAttributes objectForKey:UITextAttributeFont];
    if (font) {
        self.font = font;
    }
    UIColor *textColor = [numberTextAttributes objectForKey:UITextAttributeTextColor];
    if (textColor) {
        self.textColor = textColor;
    }
    UIColor *textShadowColor = [numberTextAttributes objectForKey:UITextAttributeTextShadowColor];
    if (textShadowColor) {
        self.shadowColor = textShadowColor;
    }
    NSValue *shadowOffsetValue = [numberTextAttributes objectForKey:UITextAttributeTextShadowOffset];
    if (shadowOffsetValue) {
        UIOffset shadowOffset = [shadowOffsetValue UIOffsetValue];
        self.shadowOffset = CGSizeMake(shadowOffset.horizontal, shadowOffset.vertical);
    }
}

В категории UILabel:

@interface UILabel (UISS)

- (void)setTextAttributes:(NSDictionary *)numberTextAttributes UI_APPEARANCE_SELECTOR;

@end

Я все еще пытаюсь понять, почему оригинальный сеттер не работает.

person Robert Wijas    schedule 22.12.2012
comment
Мы создаем пользовательские свойства в подклассе, которые работают с прокси внешнего вида. Например. свойство titleLabelFont для переноса свойства titleLabel.font UIButton. Это работает и для других свойств, таких как тени. - person Dan Reese; 15.01.2013
comment
@robert +1 очень хорошее решение. Мне потребовалось несколько часов кодирования, пока я не нашел ваш ответ. Спасибо за это. Теперь все работает нормально (и просто). - person anneblue; 22.08.2013
comment
Отличное решение, это хорошо работает и в других представлениях, таких как UIButton для установки шрифта titleLabel, поскольку Apple устарела с помощью метода setFont:. - person Michael Gaylord; 19.09.2013
comment
да, но я не могу изменить цвет текста после некоторых других UILabels - person Injectios; 27.01.2016

У меня была точно такая же проблема, но в Swift. Пользовательский внешний вид UILabel будет работать, если он будет добавлен из раскадровки, но не из кода.

Вот решение, которое я нашел в Swift, которое работает для меня:

class MyLabel: UILabel { }

extension UILabel {
    @objc dynamic var customFont: UIFont! {
        get { return self.font }
        set { self.font = newValue }
    }

    @objc dynamic var customColor: UIColor! {
        get { return self.textColor }
        set {  self.textColor = newValue }
    }
}

Затем добавьте эти строки, где вы настраиваете внешний вид вашего приложения:

MyLabel.appearance().customFont = UIFont.systemFont(ofSize: 20)
MyLabel.appearance().customColor = UIColor.magenta
person kwahn    schedule 01.02.2019
comment
Я боролся с проблемой в течение нескольких часов. Это действительно быстрое и рабочее решение! - person xZenon; 19.03.2019

Решение @robert.wijas отлично работает!

Для iOS 7 и выше мне пришлось обновить ключ, поскольку тот, который он использовал, устарел для 7+:

- (void)setTextAttributes:(NSDictionary *)numberTextAttributes;
{
    UIFont *font = [numberTextAttributes objectForKey:NSFontAttributeName];
    if (font) {
        self.font = font;
    }
    UIColor *textColor = [numberTextAttributes objectForKey:NSForegroundColorAttributeName];
    if (textColor) {
        self.textColor = textColor;
    }
    UIColor *textShadowColor = [numberTextAttributes objectForKey:NSShadowAttributeName];
    if (textShadowColor) {
        self.shadowColor = textShadowColor;
    }
    NSValue *shadowOffsetValue = [numberTextAttributes objectForKey:NSShadowAttributeName];
    if (shadowOffsetValue) {
        UIOffset shadowOffset = [shadowOffsetValue UIOffsetValue];
        self.shadowOffset = CGSizeMake(shadowOffset.horizontal, shadowOffset.vertical);
    }
}
person Matthieu Riegler    schedule 24.11.2014
comment
Обратите внимание, что вы неправильно обрабатываете атрибут NSShadowAttributeName; его значение равно NSShadow. - person Andrey Tarantsov; 09.12.2014

Обходной путь, который я использовал, - вручную применить цвет из установленного внешнего вида:

let label = UILabel()
label.textColor = UILabel.appearance().textColor

Таким образом, вам не нужно ссылаться на что-то новое или явно определять цвет. Это также работает для контекстной окраски:

label.textColor = UILabel.appearance(whenContainedInInstancesOf:[MyView.self]).textColor
person Jbryson    schedule 02.04.2018