153 lines
6.6 KiB
Swift
153 lines
6.6 KiB
Swift
//
|
|
// Copyright 2023 Signal Messenger, LLC
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
//
|
|
|
|
import SignalServiceKit
|
|
import UIKit
|
|
|
|
public extension UIFont {
|
|
|
|
// MARK: - Icon Font
|
|
|
|
class func awesomeFont(ofSize size: CGFloat) -> UIFont {
|
|
return UIFont(name: "FontAwesome", size: size)!
|
|
}
|
|
|
|
// MARK: -
|
|
|
|
class func regularFont(ofSize size: CGFloat) -> UIFont {
|
|
return .systemFont(ofSize: size, weight: .regular)
|
|
}
|
|
|
|
/// Returns an instance of the system font with scaling for the user's
|
|
/// selected content size category with the specified size being used for
|
|
/// the standard Dynamic Type size.
|
|
///
|
|
/// - Parameters:
|
|
/// - standardSize: The size the font should be for the standard Dynamic Type size.
|
|
/// - weight: The weight of the font.
|
|
/// - Returns: The system font scaled to the current Dynamic Type setting.
|
|
class func dynamicTypeFont(
|
|
ofStandardSize standardSize: CGFloat,
|
|
weight: UIFont.Weight = .regular
|
|
) -> UIFont {
|
|
return UIFontMetrics.default.scaledFont(for: .systemFont(ofSize: standardSize, weight: weight))
|
|
}
|
|
|
|
class func semiboldFont(ofSize size: CGFloat) -> UIFont {
|
|
return .systemFont(ofSize: size, weight: .semibold)
|
|
}
|
|
|
|
class func monospacedDigitFont(ofSize size: CGFloat) -> UIFont {
|
|
return .monospacedDigitSystemFont(ofSize: size, weight: .regular)
|
|
}
|
|
|
|
// MARK: - Dynamic Type
|
|
|
|
class var dynamicTypeTitle1: UIFont { UIFont.preferredFont(forTextStyle: .title1) }
|
|
|
|
class var dynamicTypeTitle2: UIFont { UIFont.preferredFont(forTextStyle: .title2) }
|
|
|
|
class var dynamicTypeTitle3: UIFont { UIFont.preferredFont(forTextStyle: .title3) }
|
|
|
|
class var dynamicTypeHeadline: UIFont { UIFont.preferredFont(forTextStyle: .headline) }
|
|
|
|
class var dynamicTypeBody: UIFont { UIFont.preferredFont(forTextStyle: .body) }
|
|
|
|
class var dynamicTypeBody2: UIFont { UIFont.preferredFont(forTextStyle: .subheadline) }
|
|
|
|
class var dynamicTypeCallout: UIFont { UIFont.preferredFont(forTextStyle: .callout) }
|
|
|
|
class var dynamicTypeSubheadline: UIFont { UIFont.preferredFont(forTextStyle: .subheadline) }
|
|
|
|
class var dynamicTypeFootnote: UIFont { UIFont.preferredFont(forTextStyle: .footnote) }
|
|
|
|
class var dynamicTypeCaption1: UIFont { UIFont.preferredFont(forTextStyle: .caption1) }
|
|
|
|
class var dynamicTypeCaption2: UIFont { UIFont.preferredFont(forTextStyle: .caption2) }
|
|
|
|
// MARK: - Dynamic Type Clamped
|
|
|
|
// We clamp the dynamic type sizes at the max size available
|
|
// without "larger accessibility sizes" enabled.
|
|
static private var maxPointSizeMap: [UIFont.TextStyle: CGFloat] = [
|
|
.title1: 34,
|
|
.title2: 28,
|
|
.title3: 26,
|
|
.headline: 23,
|
|
.body: 23,
|
|
.callout: 22,
|
|
.subheadline: 21,
|
|
.footnote: 19,
|
|
.caption1: 18,
|
|
.caption2: 17,
|
|
.largeTitle: 40
|
|
]
|
|
|
|
private class func preferredFontClamped(forTextStyle textStyle: UIFont.TextStyle) -> UIFont {
|
|
// From the documentation of -[id<UIContentSizeCategoryAdjusting> adjustsFontForContentSizeCategory:]
|
|
// Dynamic sizing is only supported with fonts that are:
|
|
// a. Vended using UIFont.preferredFont(forTextStyle:)
|
|
// b. Vended from UIFontMetrics.scaledFont(for:) or one of its variants
|
|
//
|
|
// If we clamp fonts by checking the resulting point size and then creating a new, smaller UIFont with
|
|
// a fallback max size, we'll lose dynamic sizing. Max sizes can be specified using UIFontMetrics though.
|
|
//
|
|
// UIFontMetrics will only operate on unscaled fonts. So we do this dance to cap the system default styles
|
|
// 1. Grab the standard, unscaled font by using the default trait collection
|
|
// 2. Use UIFontMetrics to scale it up, capped at the desired max size
|
|
let defaultTraitCollection = UITraitCollection(preferredContentSizeCategory: .large)
|
|
let unscaledFont = UIFont.preferredFont(forTextStyle: textStyle, compatibleWith: defaultTraitCollection)
|
|
|
|
let desiredStyleMetrics = UIFontMetrics(forTextStyle: textStyle)
|
|
guard let maxPointSize = maxPointSizeMap[textStyle] else {
|
|
owsFailDebug("Missing max point size for style: \(textStyle)")
|
|
return desiredStyleMetrics.scaledFont(for: unscaledFont)
|
|
}
|
|
return desiredStyleMetrics.scaledFont(for: unscaledFont, maximumPointSize: maxPointSize)
|
|
}
|
|
|
|
class var dynamicTypeLargeTitle1Clamped: UIFont { preferredFontClamped(forTextStyle: .largeTitle) }
|
|
class var dynamicTypeTitle1Clamped: UIFont { preferredFontClamped(forTextStyle: .title1) }
|
|
class var dynamicTypeTitle2Clamped: UIFont { preferredFontClamped(forTextStyle: .title2) }
|
|
class var dynamicTypeTitle3Clamped: UIFont { preferredFontClamped(forTextStyle: .title3) }
|
|
class var dynamicTypeHeadlineClamped: UIFont { preferredFontClamped(forTextStyle: .headline) }
|
|
class var dynamicTypeBodyClamped: UIFont { preferredFontClamped(forTextStyle: .body) }
|
|
class var dynamicTypeBody2Clamped: UIFont { preferredFontClamped(forTextStyle: .subheadline) }
|
|
class var dynamicTypeCalloutClamped: UIFont { preferredFontClamped(forTextStyle: .callout) }
|
|
class var dynamicTypeSubheadlineClamped: UIFont { preferredFontClamped(forTextStyle: .subheadline) }
|
|
class var dynamicTypeFootnoteClamped: UIFont { preferredFontClamped(forTextStyle: .footnote) }
|
|
class var dynamicTypeCaption1Clamped: UIFont { preferredFontClamped(forTextStyle: .caption1) }
|
|
class var dynamicTypeCaption2Clamped: UIFont { preferredFontClamped(forTextStyle: .caption2) }
|
|
|
|
// MARK: -
|
|
|
|
func italic() -> UIFont {
|
|
guard let fontDescriptor = fontDescriptor.withSymbolicTraits(.traitItalic) else { return self }
|
|
return UIFont(descriptor: fontDescriptor, size: 0)
|
|
}
|
|
|
|
func medium() -> UIFont {
|
|
let fontTraits = [UIFontDescriptor.TraitKey.weight: UIFont.Weight.medium]
|
|
let fontDescriptor = fontDescriptor.addingAttributes([.traits: fontTraits])
|
|
return UIFont(descriptor: fontDescriptor, size: 0)
|
|
}
|
|
|
|
func semibold() -> UIFont {
|
|
let fontTraits = [UIFontDescriptor.TraitKey.weight: UIFont.Weight.semibold]
|
|
let fontDescriptor = fontDescriptor.addingAttributes([.traits: fontTraits])
|
|
return UIFont(descriptor: fontDescriptor, size: 0)
|
|
}
|
|
|
|
func bold() -> UIFont {
|
|
let fontTraits = [UIFontDescriptor.TraitKey.weight: UIFont.Weight.bold]
|
|
let fontDescriptor = fontDescriptor.addingAttributes([.traits: fontTraits])
|
|
return UIFont(descriptor: fontDescriptor, size: 0)
|
|
}
|
|
|
|
func monospaced() -> UIFont {
|
|
return .monospacedDigitFont(ofSize: pointSize)
|
|
}
|
|
|
|
}
|