Skip to content

Commit 44a78e1

Browse files
Mark Pospeselmpospese
authored andcommitted
[Issue 63] Don’t manually increase weight of scaled system fonts
1 parent f549e21 commit 44a78e1

File tree

3 files changed

+124
-4
lines changed

3 files changed

+124
-4
lines changed

Sources/YMatterType/Typography/FontFamily/SystemFontFamily.swift

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,40 @@ public struct SystemFontFamily: FontFamily {
6464
pointSize: CGFloat,
6565
compatibleWith traitCollection: UITraitCollection?
6666
) -> UIFont {
67-
// The system font cannot be retrieved using UIFont.font(name:size:), but
68-
// instead must be created using UIFont.systemFont(ofSize:weight:)
69-
let useBoldFont = isBoldTextEnabled(compatibleWith: traitCollection)
67+
// When UIAccessibility.isBoldTextEnabled == true, then we don't need to manually
68+
// adjust the weight because the system will do it for us.
69+
let useBoldFont = isBoldTextEnabled(compatibleWith: traitCollection) && !UIAccessibility.isBoldTextEnabled
7070
let actualWeight = useBoldFont ? accessibilityBoldWeight(for: weight) : weight
7171

72+
// The system font cannot be retrieved using UIFont.font(name:size:), but
73+
// instead must be created using UIFont.systemFont(ofSize:weight:)
7274
return UIFont.systemFont(ofSize: pointSize, weight: actualWeight.systemWeight)
7375
}
76+
77+
/// Returns the next heavier supported weight (if any), otherwise the heaviest supported weight
78+
public func accessibilityBoldWeight(for weight: Typography.FontWeight) -> Typography.FontWeight {
79+
var boldWeight: Typography.FontWeight
80+
81+
switch weight {
82+
// For 3 lightest weights, move up 1 weight
83+
case .ultralight:
84+
boldWeight = .thin
85+
case .thin:
86+
boldWeight = .light
87+
case .light:
88+
boldWeight = .regular
89+
90+
// For all remaining weights, move up 2 weights
91+
case .regular:
92+
boldWeight = .semibold
93+
case .medium:
94+
boldWeight = .bold
95+
case .semibold:
96+
boldWeight = .heavy
97+
case .bold, .heavy, .black:
98+
boldWeight = .black
99+
}
100+
101+
return boldWeight
102+
}
74103
}

Sources/YMatterType/Typography/Typography+Font.swift

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,14 @@ extension Typography {
127127

128128
private extension Typography {
129129
func generateFixedFont(compatibleWith traitCollection: UITraitCollection?) -> UIFont {
130-
fontFamily.font(for: fontWeight, pointSize: fontSize, compatibleWith: traitCollection)
130+
var traits = traitCollection
131+
if !isFixed && fontFamily is SystemFontFamily {
132+
// System font already considers accessibility BoldText when
133+
// we get the scaled font via `UIFontMetrics.scaledFont(for:compatibleWith:)`,
134+
// so we pass a non-bold trait collection when generating the fixed font, so
135+
// as not to increase the font weight twice.
136+
traits = UITraitCollection(legibilityWeight: .regular)
137+
}
138+
return fontFamily.font(for: fontWeight, pointSize: fontSize, compatibleWith: traits)
131139
}
132140
}

Tests/YMatterTypeTests/Typography/FontFamily/SystemFontFamilyTests.swift

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,60 @@ final class SystemFontFamilyTests: XCTestCase {
2929
let (sut, _, _) = makeSUT()
3030
XCTAssertTrue(sut.familyName.isEmpty)
3131
}
32+
33+
func test_accessibilityWeight_deliversCorrectWeight() {
34+
let (sut, _, _) = makeSUT()
35+
let lightWeights: [Typography.FontWeight] = [.ultralight, .thin, .light]
36+
let midWeights: [Typography.FontWeight] = [.regular, .medium, .semibold]
37+
let heavyWeights: [Typography.FontWeight] = [.bold, .heavy, .black]
38+
39+
for weight in lightWeights {
40+
XCTAssertEqual(sut.accessibilityBoldWeight(for: weight).rawValue, weight.rawValue + 100)
41+
}
42+
43+
for weight in midWeights {
44+
XCTAssertEqual(sut.accessibilityBoldWeight(for: weight).rawValue, weight.rawValue + 200)
45+
}
46+
47+
for weight in heavyWeights {
48+
XCTAssertEqual(sut.accessibilityBoldWeight(for: weight), .black)
49+
}
50+
}
51+
52+
func test_layout_deliversCorrectFont() throws {
53+
let (sut, _, traitCollections) = makeSUT()
54+
55+
for weight in Typography.systemFamily.supportedWeights {
56+
for traitCollection in traitCollections {
57+
let typography = Typography(fontFamily: sut, fontWeight: weight, fontSize: 16, lineHeight: 24)
58+
let layoutDynamic = typography.generateLayout(compatibleWith: traitCollection)
59+
let layoutFixed = typography.fixed.generateLayout(compatibleWith: traitCollection)
60+
XCTAssertEqual(layoutDynamic.font.fontName, layoutFixed.font.fontName)
61+
}
62+
}
63+
}
64+
65+
func test_layoutWithLegibilityWeightBold_deliversHeavierFont() throws {
66+
// Given
67+
let (sut, _, _) = makeSUT()
68+
let traitRegular = UITraitCollection(legibilityWeight: .regular)
69+
let traitBold = UITraitCollection(legibilityWeight: .bold)
70+
var weights = Typography.systemFamily.supportedWeights
71+
weights.removeLast() // don't consider .black because we cannot go heavier
72+
73+
for weight in weights {
74+
// When
75+
let typography = Typography(fontFamily: sut, fontWeight: weight, fontSize: 16, lineHeight: 24)
76+
let layoutRegular = typography.generateLayout(compatibleWith: traitRegular)
77+
let layoutBold = typography.generateLayout(compatibleWith: traitBold)
78+
79+
let weightRegular = try XCTUnwrap(self.weight(from: layoutRegular.font))
80+
let weightBold = try XCTUnwrap(self.weight(from: layoutBold.font))
81+
82+
// Then
83+
XCTAssertGreaterThan(weightBold.rawValue, weightRegular.rawValue)
84+
}
85+
}
3286
}
3387

3488
// We use large tuples in makeSUT()
@@ -46,4 +100,33 @@ private extension SystemFontFamilyTests {
46100
]
47101
return (sut, pointSizes, traitCollections)
48102
}
103+
104+
func weight(from font: UIFont) -> Typography.FontWeight? {
105+
guard let weightString = font.fontName.components(separatedBy: "-").last else {
106+
return nil
107+
}
108+
109+
switch weightString.lowercased(with: Locale(identifier: "en_US")) {
110+
case "ultralight", "extralight":
111+
return .ultralight
112+
case "thin":
113+
return .thin
114+
case "light":
115+
return .light
116+
case "regular":
117+
return .regular
118+
case "medium":
119+
return .medium
120+
case "semibold", "demibold":
121+
return .semibold
122+
case "bold":
123+
return .bold
124+
case "heavy", "extrabold":
125+
return .heavy
126+
case "black":
127+
return .black
128+
default:
129+
return nil
130+
}
131+
}
49132
}

0 commit comments

Comments
 (0)