通过 UIFontDescriptor 创建 UIFont 时,UIFontWeightTrait 和 UIFontDescriptorFamilyAttribute 属性被忽略。该问题涉及IT技术。

21

给定以下代码和运行iOS 7.1或更高版本的设备:

 NSDictionary *fontTraitsDictionary = @{UIFontWeightTrait : @(-1.0)};
 NSDictionary *attributesDictionary = @{
                                       UIFontDescriptorFamilyAttribute : @"Helvetica Neue", 
                                       UIFontDescriptorTraitsAttribute : fontTraitsDictionary
                                       };
 UIFontDescriptor *ultraLightDescriptor = [UIFontDescriptor fontDescriptorWithFontAttributes:attributesDictionary];
 UIFont *shouldBeAnUltraLightFont = [UIFont fontWithDescriptor:ultraLightDescriptor size:24];

 NSLog(@"%@", shouldBeAnUltraLightFont);

我期望shouldBeAnUltraLightFont的值是HelveticaNeue-UltraLight的实例,但实际上它是:

<UICTFont: 0x908d160> font-family: "Helvetica"; font-weight: normal; font-style: normal; font-size: 24.00pt

据我理解,我遵循了苹果的文档。为什么字体系列和字重信息完全被忽略了?

我尝试过的方法

  • 我尝试了其他像Helvetica、Avenir等家族名称。
  • 我尝试了在有效范围从-1到1的其他字重,以0.25为增量。

无论做出这些更改,返回的字体总是普通字重的Helvetica的基本实例。


如果从字典中删除UIFontDescriptorTraitsAttribute键,则生成的字体将是正确的字体族(“Helvetica Neue”,而不是“Helvetica”)。这可能是一个错误。更有可能的是,您应该使用fontDescriptorWithSymbolicTraits来获取正确的字体,但是UIFontDescriptorSymbolicTraits缺少轻/超轻的值。这可能是一个疏忽。 - blork
在8.0b4版本中,Helvetica Neue被返回(<UICTFont:0x7b22c690> font-family:"Helvetica Neue";font-weight:normal;font-style:normal;font-size:24.00pt),因此已经有了一些进展。 - Arek Holko
6
在我看来,您的代码似乎没有问题,而是iOS字体实现有问题。如果Helvetica Neue的每个变体实际上是具有正常字重的自己的字体,则寻找轻量级变体将不会成功。这是HelveticaNeue-UltraLight的日志,按名称检索: <UICTFont:0x7bf91af0> font-family:“HelveticaNeue-UltraLight”;font-weight:normal;font-style:normal;font-size:16.00pt - Nate Cook
5个回答

12

我遇到了同样的问题,而且文档并没有太多帮助。最终我发现结合使用“family”属性和“face”属性可以解决问题:

 UIFontDescriptor* desc = [UIFontDescriptor fontDescriptorWithFontAttributes:
        @{
            UIFontDescriptorFamilyAttribute: @"Helvetica Neue",
            UIFontDescriptorFaceAttribute: @"Light"
        }
    ];

选择人脸是有效的,我首先使用了您的建议,然后选择了'fontDescriptorWithFace'。似乎并没有真正记录面值。 - lazi74

7

从文档中得知:

Font Traits Dictionary Keys

The following constants can be used as keys to retrieve information about a font descriptor from its trait dictionary.

NSString *const UIFontSymbolicTrait;
NSString *const UIFontWeightTrait;
NSString *const UIFontWidthTrait;
NSString *const UIFontSlantTrait;
我觉得这些键似乎只设计用于从字体描述符中获取信息,而不是用于设置它。例如,您可以执行以下操作:
UIFont *font = [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline];
NSDictionary *traits = [font.fontDescriptor objectForKey:UIFontDescriptorTraitsAttribute];
CGFloat weight = [traits[UIFontWeightTrait] floatValue];

这会向您显示得到的字体比正常字体略重。这似乎不如能够请求较轻字体而无需给出精确名称有用—但这似乎是预期使用方式。


if (font.fontDescriptor.symbolicTraits & UIFontDescriptorTraitBold) // 直接测试是否为粗体 - Richie Hyatt

2

当寻找粗体或斜体字体时,符号特征至少似乎得到了尊重。因此,这很好用:

+ (UIFont*)fontWithFamily:(NSString*)family bold:(BOOL)bold italic:(BOOL)italic size:(CGFloat)pointSize {
  UIFontDescriptorSymbolicTraits traits = 0;
  if (bold)   traits |= UIFontDescriptorTraitBold;
  if (italic) traits |= UIFontDescriptorTraitItalic;
  UIFontDescriptor* fd = [UIFontDescriptor
                          fontDescriptorWithFontAttributes:@{UIFontDescriptorFamilyAttribute: family,
                                                             UIFontDescriptorTraitsAttribute: @{UIFontSymbolicTrait:
                                                                                                  [NSNumber numberWithInteger:traits]}}];
  NSArray* matches = [fd matchingFontDescriptorsWithMandatoryKeys:
                      [NSSet setWithObjects:UIFontDescriptorFamilyAttribute, UIFontDescriptorTraitsAttribute, nil]];
  if (matches.count == 0) return nil;
  return [UIFont fontWithDescriptor:matches[0] size:pointSize];
}

例如:[MyClass fontWithFamily:@"Avenir Next Condensed" bold:YES italic:NO size:12.0f];

我认为这并没有帮助到OP,因为他特别需要一个带有UIFontWeightTrait的轻字体,但这可能会帮助其他遇到类似问题的人。


0

你可以使用CTFontDescriptorCreateCopyWithVariation,但是你必须先找到字体重量的变化轴标识符。我已经使用以下方法在react-native-svg中实现了对可变字体重量的支持:

- (CTFontRef)getGlyphFont
{
    NSString *fontFamily = topFont_->fontFamily;
    NSNumber *fontSize = [NSNumber numberWithDouble:topFont_->fontSize];

    NSString *fontWeight = RNSVGFontWeightStrings[topFont_->fontWeight];
    NSString *fontStyle = RNSVGFontStyleStrings[topFont_->fontStyle];

    BOOL fontFamilyFound = NO;
    NSArray *supportedFontFamilyNames = [UIFont familyNames];

    if ([supportedFontFamilyNames containsObject:fontFamily]) {
        fontFamilyFound = YES;
    } else {
        for (NSString *fontFamilyName in supportedFontFamilyNames) {
            if ([[UIFont fontNamesForFamilyName: fontFamilyName] containsObject:fontFamily]) {
                fontFamilyFound = YES;
                break;
            }
        }
    }
    fontFamily = fontFamilyFound ? fontFamily : nil;

    UIFont *font = [RCTFont updateFont:nil
                              withFamily:fontFamily
                                    size:fontSize
                                  weight:fontWeight
                                   style:fontStyle
                                 variant:nil
                         scaleMultiplier:1.0];

    CTFontRef ref = (__bridge CTFontRef)font;

    int weight = topFont_->absoluteFontWeight;
    if (weight == 400) {
        return ref;
    }

    CFArrayRef cgAxes = CTFontCopyVariationAxes(ref);
    CFIndex cgAxisCount = CFArrayGetCount(cgAxes);
    CFNumberRef wght_id = 0;

    for (CFIndex i = 0; i < cgAxisCount; ++i) {
        CFTypeRef cgAxis = CFArrayGetValueAtIndex(cgAxes, i);
        if (CFGetTypeID(cgAxis) != CFDictionaryGetTypeID()) {
            continue;
        }

        CFDictionaryRef cgAxisDict = (CFDictionaryRef)cgAxis;
        CFTypeRef axisName = CFDictionaryGetValue(cgAxisDict, kCTFontVariationAxisNameKey);
        CFTypeRef axisId = CFDictionaryGetValue(cgAxisDict, kCTFontVariationAxisIdentifierKey);

        if (!axisName || CFGetTypeID(axisName) != CFStringGetTypeID()) {
            continue;
        }
        CFStringRef axisNameString = (CFStringRef)axisName;
        NSString *axisNameNSString = (__bridge NSString *)(axisNameString);
        if (![@"Weight" isEqualToString:axisNameNSString]) {
            continue;
        }

        if (!axisId || CFGetTypeID(axisId) != CFNumberGetTypeID()) {
            continue;
        }
        wght_id = (CFNumberRef)axisId;
        break;
    }

    if (wght_id == 0) {
        return ref;
    }
    UIFontDescriptor *uifd = font.fontDescriptor;
    CTFontDescriptorRef ctfd = (__bridge CTFontDescriptorRef)(uifd);
    CTFontDescriptorRef newfd = CTFontDescriptorCreateCopyWithVariation(ctfd, wght_id, (CGFloat)weight);
    CTFontRef newfont = CTFontCreateCopyWithAttributes(ref, (CGFloat)[fontSize doubleValue], nil, newfd);
    return newfont;
} 

https://github.com/react-native-community/react-native-svg/blob/bf0adb4a8206065ecb9e7cdaa18c3140d24ae338/ios/Text/RNSVGGlyphContext.m#L137-L235


0

这对我有用:

  [maString
   enumerateAttribute:NSFontAttributeName
   inRange:<-- desired range -->
   options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired
   usingBlock:^(id  _Nullable value, NSRange range, BOOL *_Nonnull stop) {
     if (![value isKindOfClass:[UIFont class]]) {
       return;
     }
     UIFontDescriptor *fontDescriptor = ((UIFont *)value).fontDescriptor;
     NSMutableDictionary *traits = [[fontDescriptor.fontAttributes objectForKey:UIFontDescriptorTraitsAttribute] mutableCopy] ?: [NSMutableDictionary new];
     traits[UIFontWeightTrait] = @(UIFontWeightSemibold);
     fontDescriptor = [fontDescriptor fontDescriptorByAddingAttributes:@{UIFontDescriptorTraitsAttribute : traits}];
     UIFont *semiboldFont = [UIFont fontWithDescriptor:fontDescriptor size:fontDescriptor.pointSize];
     if (semiboldFont) {
       [maString addAttribute:NSFontAttributeName value:semiboldFont range:range];
     }
   }];

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