Skip to content

Commit

Permalink
Add supportedWeights property
Browse files Browse the repository at this point in the history
  • Loading branch information
Mark Pospesel authored and mpospese committed Sep 23, 2022
1 parent 70cd367 commit b3f1017
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 26 deletions.
20 changes: 17 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,13 @@ try UIFont.register(
Because `Bundle.module` is only available from within your Swift package, we recommend that you expose a helper method from within your Swift package to register the fonts and that internally references `Bundle.module`.

```
public struct NotoSansFontFamily {
public struct NotoSansFontFamily: FontFamily {
/// Font family root name
let familyName: String = "NotoSans"
// We've only bundled 3 weights for this font
var supportedWeights: [Typography.FontWeight] = [.regular, .medium, .semibold]
/// Register all 3 NotoSans fonts
public static func registerFonts() throws {
let names = makeFontNames()
Expand Down Expand Up @@ -138,7 +144,9 @@ public struct NotoSansFontFamily {
```
## Which font weights to include

Fonts can come in up to 9 different weights, ranging from ultralight (100) to black (900), but not all font families will support every weight. Also you might not wish to include fonts for weights which your design system does not use in order to keep your bundle size as small as possible. However, in order to support the Accessibility Bold Text feature (which allows users to request a heavier weight font), for each font weight in your design system, you need to include the next heavier font weight as well. For example, if your design system only uses regular (400) and bold (700) weight fonts, you would need to include (and register) font files for regular (400), medium (500), bold (700), and heavy (800) weight fonts.
Fonts can come in up to 9 different weights, ranging from ultralight (100) to black (900), but not all font families will support every weight. Also you might not wish to include fonts for weights which your design system does not use in order to keep your bundle size as small as possible. However, in order to support the Accessibility Bold Text feature (which allows users to request a heavier weight font), for each font weight in your design system, you should include the next heavier font weight as well. For example, if your design system only uses regular (400) and bold (700) weight fonts, if possible you should include (and register) font files for regular (400), medium (500), bold (700), and heavy (800) weight fonts.

When Accessibility Bold Text is enabled, `FontFamily` will use the next heavier font weight listed in `supportedWeights` (if any), and otherwise use the heaviest supported font weight.

## Using System Fonts

Expand All @@ -159,7 +167,9 @@ extension Typography {

## Custom Font Families

Y—MatterType does its best to automatically map font family name, font style (regular or italic), and font weight (ultralight to black) into the registered name of the font so that it may be loaded using `UIFont(name:, size:)`. (This registered font name may differ from the name of the font file and from the display name for the font family.) However, some font families may require custom behavior in order to properly load the font (e.g. the semibold font weight might be named "DemiBold" instead of the more common "SemiBold"). To support this you can declare a class or struct that conforms to the `FontFamily` protocol and use that to initialize your `Typography` instance. This protocol has four methods, each of which may be optionally overridden to customize how fonts of a given weight are loaded. The framework contains two different implementations of `FontFamily` for you to consider (`DefaultFontFamily` and `SystemFontFamily`).
Y—MatterType does its best to automatically map font family name, font style (regular or italic), and font weight (ultralight to black) into the registered name of the font so that it may be loaded using `UIFont(name:, size:)`. (This registered font name may differ from the name of the font file and from the display name for the font family.) However, some font families may require custom behavior in order to properly load the font (e.g. the semibold font weight might be named "DemiBold" instead of the more common "SemiBold"). Or your font family might not include all 9 possible font weights. To support this you can declare a class or struct that conforms to the `FontFamily` protocol and use that to initialize your `Typography` instance. This protocol has four methods, each of which may be optionally overridden to customize how fonts of a given weight are loaded. The `supportedWeights` property that can be overridden. If your font family does not have access to all 9 font weights, then you should override `supportedWeights` and return the weights of all fonts bundled in your project.

The framework contains two different implementations of `FontFamily` for you to consider (`DefaultFontFamily` and `SystemFontFamily`).

In the event that the requested font cannot be loaded (either the name is incorrect or it was not registered), Y—MatterType will fall back to loading a system font of the specified point size and weight.

Expand All @@ -168,6 +178,10 @@ struct AppleSDGothicNeoInfo: FontFamily {
/// Font family root name
let familyName: String = "AppleSDGothicNeo"
// This font family doesn't support weights higher than Bold
var supportedWeights: [Typography.FontWeight] =
[.ultralight, .thin, .light, .regular, .medium, .semibold, .bold]
/// Generates a weight name suffix as part of a full font name. Not all fonts support all 9 weights.
/// - Parameter weight: desired font weight
/// - Returns: The weight name to use
Expand Down
39 changes: 17 additions & 22 deletions Sources/YMatterType/Typography/FontFamily/FontFamily.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,16 @@ public protocol FontFamily {
/// e.g. "Italic" is a typical suffix for italic fonts.
/// default = ""
var fontNameSuffix: String { get }


/// All font weights supported by this font family (or those that you choose to bundle in your project).
///
/// Defaults to all 9 font weights, but can be overridden in custom implementations of `FontFamily`.
/// You must support at least one weight.
/// This is used in the default implementation of `accessibilityBoldWeight(for:)`
var supportedWeights: [Typography.FontWeight] { get }

// The following four methods have default implementations that
// can be overridden in custom implementations of FontFamily
// can be overridden in custom implementations of `FontFamily`.

/// Returns a font for the specified `weight` and `pointSize` that is compatible with the `traitCollection`
/// - Parameters:
Expand Down Expand Up @@ -64,8 +71,12 @@ extension Typography {
// MARK: - Default implementations

extension FontFamily {
// returns no suffix
public var fontNameSuffix: String { "" }

// returns all weights
public var supportedWeights: [Typography.FontWeight] { Typography.FontWeight.allCases }

public func font(
for weight: Typography.FontWeight,
pointSize: CGFloat,
Expand Down Expand Up @@ -120,26 +131,10 @@ extension FontFamily {
}

public func accessibilityBoldWeight(for weight: Typography.FontWeight) -> Typography.FontWeight {
// By default we will move up 1 weight when Accessibility Bold is enabled
// (Override to transform to different weights or only to those weighs available for a specific font family.)
switch weight {
case .ultralight:
return .thin
case .thin:
return .light
case .light:
return .regular
case .regular:
return .medium
case .medium:
return .semibold
case .semibold:
return .bold
case .bold:
return .heavy
case .heavy, .black:
return .black
}
// By default returns the next heavier supported weight (if any), otherwise the heaviest supported weight
let weights = supportedWeights.sorted(by: { $0.rawValue < $1.rawValue })
// return the next heavier supported weight
return weights.first(where: { $0.rawValue > weight.rawValue }) ?? weights.last ?? weight
}

/// Determines whether the accessibility Bold Text feature is enabled within the given trait collection.
Expand Down
26 changes: 26 additions & 0 deletions Tests/YMatterTypeTests/Typography/FontFamily/FontFamilyTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,30 @@ final class FontFamilyTests: XCTestCase {
XCTAssertEqual(sut.accessibilityBoldWeight(for: $0).rawValue, expectedWeight)
}
}

func testSupportedWeights() {
let sut = MockFontFamily()
sut.supportedWeights = [.light, .semibold, .heavy]

// Given any weight, we expect it to return the next heavier supported weight
XCTAssertEqual(sut.accessibilityBoldWeight(for: .ultralight), .light)
XCTAssertEqual(sut.accessibilityBoldWeight(for: .thin), .light)
XCTAssertEqual(sut.accessibilityBoldWeight(for: .light), .semibold)
XCTAssertEqual(sut.accessibilityBoldWeight(for: .regular), .semibold)
XCTAssertEqual(sut.accessibilityBoldWeight(for: .medium), .semibold)
XCTAssertEqual(sut.accessibilityBoldWeight(for: .semibold), .heavy)
XCTAssertEqual(sut.accessibilityBoldWeight(for: .bold), .heavy)

// If there is no heavier weight, then we expect it to return the heaviest weight
XCTAssertEqual(sut.accessibilityBoldWeight(for: .heavy), .heavy)
XCTAssertEqual(sut.accessibilityBoldWeight(for: .black), .heavy)

// Given no supported weights we expect it to return the weight passed in
sut.supportedWeights = []
Typography.FontWeight.allCases.forEach {
XCTAssertEqual(sut.accessibilityBoldWeight(for: $0), $0)
}
}

func testCompatibleTraitCollection() {
let (sut, _, _) = makeSUT()
Expand Down Expand Up @@ -119,4 +143,6 @@ private extension FontFamilyTests {

final class MockFontFamily: FontFamily {
let familyName: String = "MockSerifMono"

var supportedWeights: [Typography.FontWeight] = Typography.FontWeight.allCases
}
2 changes: 2 additions & 0 deletions Tests/YMatterTypeTests/Typography/TypogaphyTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ final class TypogaphyTests: XCTestCase {

struct NotoSansFontFamily: FontFamily {
let familyName = "NotoSans"

var supportedWeights: [Typography.FontWeight] { [.regular] }
}

extension Typography {
Expand Down
5 changes: 4 additions & 1 deletion Tests/YMatterTypeTests/Typography/Typography+FontTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,10 @@ final class TypographyFontTests: XCTestCase {

struct AppleSDGothicNeoInfo: FontFamily {
let familyName: String = "AppleSDGothicNeo"


// This font family doesn't support weights higher than Bold
var supportedWeights: [Typography.FontWeight] = [.ultralight, .thin, .light, .regular, .medium, .semibold, .bold]

func weightName(for weight: Typography.FontWeight) -> String {
switch weight {
case .ultralight:
Expand Down

0 comments on commit b3f1017

Please sign in to comment.