21

Given the following code and a device running iOS 7.1 or later:

 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);

I would expect the value of shouldBeAnUltraLightFont to be an instance of HelveticaNeue-UltraLight, but instead it is:

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

I am following the Apple documentation as far as I understand it. Why is the font family and font weight information completely ignored?

Things I’ve Tried

  • I've tried other family names like Helvetica, Avenir, etc.
  • I've tried other font weights in the valid range from -1 to 1, in increments of 0.25

Regardless of these changes, the font returned is always a vanilla instance of Helvetica at normal weight.

4

5 回答 5

12

我遇到了同样的问题,文档并没有太大帮助。最终我发现使用 family 属性和 face 属性是有效的:

 UIFontDescriptor* desc = [UIFontDescriptor fontDescriptorWithFontAttributes:
        @{
            UIFontDescriptorFamilyAttribute: @"Helvetica Neue",
            UIFontDescriptorFaceAttribute: @"Light"
        }
    ];
于 2014-11-26T21:58:49.537 回答
7

从文档:

字体特征字典键

以下常量可用作键以从其特征字典中检索有关字体描述符的信息。

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];

这会告诉你你得到的字体比正常的重量要重一点。这似乎并不像无需给出确切名称就可以要求更轻的字体一样有用,但这似乎是预期的用途。

于 2014-08-12T06:14:18.047 回答
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,但它可能会帮助其他有类似问题的人。

于 2014-12-01T16:11:32.937 回答
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];
     }
   }];
于 2019-08-30T13:54:30.810 回答
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

于 2019-07-20T20:05:02.303 回答