Created
April 14, 2021 01:07
-
-
Save jverkoey/e5965b664ab71b1e4eb1a0d1cbbb76ec to your computer and use it in GitHub Desktop.
Dynamic Type system fonts with custom point sizes, weight, and italics
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
static const CGFloat kFontWeightEpsilon = FLT_EPSILON; | |
@implementation UIFont (CustomizedDynamicType) | |
+ (nonnull UIFont *)preferredFontWithDefaultSize:(CGFloat)size | |
textStyle:(nonnull UIFontTextStyle)textStyle { | |
return [self preferredFontWithDefaultSize:size | |
textStyle:textStyle | |
fontWeight:UIFontWeightRegular | |
italic:NO]; | |
} | |
+ (nonnull UIFont *)preferredFontWithDefaultSize:(CGFloat)size | |
textStyle:(nonnull UIFontTextStyle)textStyle | |
fontWeight:(UIFontWeight)fontWeight | |
italic:(BOOL)italic { | |
UIFontDescriptor *fontDescriptor = | |
[UIFontDescriptor preferredFontDescriptorWithTextStyle:textStyle]; | |
BOOL weighted = fabs(fontWeight) > kFontWeightEpsilon; | |
if (@available(iOS 14, *)) { | |
if (weighted) { | |
fontDescriptor = [fontDescriptor fontDescriptorByAddingAttributes:@{ | |
UIFontDescriptorTraitsAttribute : @{UIFontWeightTrait : @(fontWeight)} | |
}]; | |
} | |
if (italic) { | |
fontDescriptor = [fontDescriptor fontDescriptorByAddingAttributes:@{ | |
UIFontDescriptorTraitsAttribute : @{UIFontSymbolicTrait : @(UIFontDescriptorTraitItalic)} | |
}]; | |
} | |
} else { | |
if (italic) { | |
BOOL isBolded = fontWeight > 0; | |
if (isBolded) { | |
// Pre-iOS 14 does not support weighted italic fonts for any weight other than bold, and the | |
// only way to configure a font in that way is to use symbolic traits, so we short-circuit | |
// the case of bold-italic fonts here. | |
UIFontDescriptor *boldItalicFontDescriptor = | |
[fontDescriptor fontDescriptorWithSymbolicTraits:UIFontDescriptorTraitItalic | | |
UIFontDescriptorTraitBold]; | |
if (boldItalicFontDescriptor != nil) { | |
fontDescriptor = boldItalicFontDescriptor; | |
} else { | |
// Bold italic didn't work, fall back to at least bold then. | |
UIFontDescriptor *boldFontDescriptor = | |
[fontDescriptor fontDescriptorWithSymbolicTraits:UIFontDescriptorTraitBold]; | |
if (boldFontDescriptor != nil) { | |
fontDescriptor = boldFontDescriptor; | |
} | |
} | |
} else { | |
// Treat all other weights as simply italic. | |
UIFontDescriptor *italicFontDescriptor = | |
[fontDescriptor fontDescriptorWithSymbolicTraits:UIFontDescriptorTraitItalic]; | |
if (italicFontDescriptor != nil) { | |
fontDescriptor = italicFontDescriptor; | |
} | |
} | |
} else if (weighted) { | |
if (@available(iOS 13, *)) { | |
// iOS 13 temporarily broke support for making bold fonts using UIFontWeightTrait, so we | |
// fall back to the system font API. This means we lose leading adjustments, but are able to | |
// preserve the expected weighting. | |
fontDescriptor = [[UIFont systemFontOfSize:size weight:fontWeight] fontDescriptor]; | |
} else { | |
// On iOS 12 we can customize font descriptors using UIFontWeightBold, so we use this | |
// approach in order to maintain UIKit's tweaked leading. | |
if (fabs(fontWeight - UIFontWeightBold) < kFontWeightEpsilon) { | |
fontDescriptor = [fontDescriptor fontDescriptorByAddingAttributes:@{ | |
UIFontDescriptorTraitsAttribute : @{UIFontWeightTrait : @(UIFontWeightBold)} | |
}]; | |
} else { | |
// But for all other weights, we need to fall back to the system font API in order to | |
// receive a font of the requested weight. This means we lose the tweaked leading values, | |
// but it's more important that we respect the requested weighting. | |
fontDescriptor = [[UIFont systemFontOfSize:size weight:fontWeight] fontDescriptor]; | |
} | |
} | |
} | |
} | |
UIFont *font = [UIFont fontWithDescriptor:fontDescriptor size:size]; | |
UIFontMetrics *fontMetrics = [UIFontMetrics metricsForTextStyle:textStyle]; | |
return [fontMetrics scaledFontForFont:font]; | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment