Skip to content

Instantly share code, notes, and snippets.

@jverkoey
Created April 14, 2021 01:07
Show Gist options
  • Save jverkoey/e5965b664ab71b1e4eb1a0d1cbbb76ec to your computer and use it in GitHub Desktop.
Save jverkoey/e5965b664ab71b1e4eb1a0d1cbbb76ec to your computer and use it in GitHub Desktop.
Dynamic Type system fonts with custom point sizes, weight, and italics
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