Unbolding в UIFontDescriptor

Я использую динамический тип в приложении и имею сценарии, где я хочу изменить внешний вид шрифта, например, сделав его курсивом или распаковав его. Добавление стиля достаточно просто:

UIFontDescriptor *descriptor = [[UIFontDescriptor preferredFontDescriptorWithTextStyle:UIFontTextStyleHeadline]
                                fontDescriptorWithSymbolicTraits:UIFontDescriptorTraitItalic];
UIFont *font = [UIFont fontWithDescriptor:descriptor size:descriptor.pointSize];

однако нет четкого механизма для удаления стиля. Я мог бы попробовать настроить атрибуты, но они выглядят еще более сложными, с полностью недокументированными API:

Regular Headline: {
    NSCTFontUIUsageAttribute = UICTFontTextStyleHeadline;
    NSFontNameAttribute = ".AppleSystemUIHeadline";
    NSFontSizeAttribute = 17;
}

Italic Headline: {
    NSCTFontUIUsageAttribute = UICTFontTextStyleItalicHeadline;
    NSFontNameAttribute = ".AppleSystemUIItalicHeadline";
    NSFontSizeAttribute = 17;
}

есть еще один путь, который я упускаю? Я мог бы использовать [UIFont systemFontWithSize:descriptor.pointSize] но я не хотите потерять все правила рисования, предоставляемые динамическим типом.

1 ответов


на fontDescriptorWithSymbolicTraits: метод на самом деле способен делать то, что вы хотите, за исключением некоторых крайних случаев в поддержке признаков шрифта среди встроенных семантических стилей текста. Ключевым понятием здесь является то, что этот метод заменяет все символьные признаки на предыдущем дескрипторе с новым признаком(признаками). документация немного wishy-washy на этом просто заявляя, что новые черты "имеют приоритет над" старыми.

побитовые операции используются для добавления и удалите определенные черты, но, похоже, при работе с дескриптором, сгенерированным preferredFontDescriptorWithTextStyle:. Не все шрифты поддерживают все черты. Например, шрифт заголовка взвешивается в соответствии с предпочтительным размером контента пользователя, и даже если вы можете удалить дескриптор его полужирного признака, соответствующий UIFont будет полужирным. К сожалению, это нигде не документировано, поэтому открытие каких-либо дополнительных нюансов остается в качестве упражнения для читатель.

следующий пример иллюстрирует эти вопросы:

// Start with a system font, in this case the headline font
// bold: YES italic: NO
UIFontDescriptor * originalDescriptor = [UIFontDescriptor preferredFontDescriptorWithTextStyle:UIFontTextStyleHeadline];
NSLog(@"originalDescriptor bold: %d italic: %d",
      isBold(originalDescriptor), isItalic(originalDescriptor));

// Try to set the italic trait. This may not be what you expected; the 
// italic trait is not added. On a normal UIFontDescriptor the italic
// trait would have been set and the bold trait unset.
// Ultimately it seems that there is no variant of the headline font that
// is italic but not bold.
// bold: YES italic: NO
UIFontDescriptor * italicDescriptor = [originalDescriptor fontDescriptorWithSymbolicTraits:UIFontDescriptorTraitItalic];
NSLog(@"italicDescriptor bold: %d italic: %d",
      isBold(italicDescriptor), isItalic(italicDescriptor));

// The correct way to make this font descriptor italic (and coincidentally
// the safe way to make any other descriptor italic without discarding its
// other traits) would be as follows:
// bold: YES italic: YES
UIFontDescriptor * boldItalicDescriptor = [originalDescriptor fontDescriptorWithSymbolicTraits:(originalDescriptor.symbolicTraits | UIFontDescriptorTraitItalic)];
NSLog(@"boldItalicDescriptor bold: %d italic: %d",
      isBold(boldItalicDescriptor), isItalic(boldItalicDescriptor));

// Your intention was to remove bold without affecting any other traits, which
// is also easy to do with bitwise logic.
// Using the originalDescriptor, remove bold by negating it then applying
// a logical AND to filter it out of the existing traits.
// bold: NO  italic: NO
UIFontDescriptor * nonBoldDescriptor = [originalDescriptor fontDescriptorWithSymbolicTraits:(originalDescriptor.symbolicTraits & ~UIFontDescriptorTraitBold)];
NSLog(@"nonBoldDescriptor bold: %d italic: %d",
      isBold(nonBoldDescriptor), isItalic(nonBoldDescriptor));

// Seems like it worked, EXCEPT there is no font that matches. Turns out
// there is no regular weight alternative for the headline style font.
// To confirm, test with UIFontDescriptorTraitsAttribute as the mandatory
// key and you'll get back a nil descriptor.
// bold: YES italic: NO
nonBoldDescriptor = [nonBoldDescriptor matchingFontDescriptorsWithMandatoryKeys:nil].firstObject;
NSLog(@"nonBoldDescriptor bold: %d italic: %d",
      isBold(nonBoldDescriptor), isItalic(nonBoldDescriptor));

напомним, что isBold и isItalic функции, использованные выше для краткости могут быть реализованы следующим образом:

 BOOL isBold(UIFontDescriptor * fontDescriptor)
 {
    return (fontDescriptor.symbolicTraits & UIFontDescriptorTraitBold) != 0;
 }

 BOOL isItalic(UIFontDescriptor * fontDescriptor)
 {
    return (fontDescriptor.symbolicTraits & UIFontDescriptorTraitItalic) != 0;
 }