-
-
Save kevin-hirsch/e5902a3d596e91010b1fe158fce743f4 to your computer and use it in GitHub Desktop.
Dynamic Type: Scaling Custom Fonts
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
# https://github.com/realm/SwiftLint#defining-custom-rules | |
custom_rules: | |
use_scaled_fonts: | |
name: "Use scaled fonts: UIFont.scaledFont(style:)" | |
regex: '\.(?!scaled)[a-zA-Z]*[fF]ont\(' | |
severity: error |
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
struct TextStyle { | |
let size: CGFloat | |
let emphasis: Emphasis | |
let name: Name | |
init(size: CGFloat, emphasis: Emphasis = .none, name: Name = .ptSans) { | |
self.size = size | |
self.emphasis = emphasis | |
self.name = name | |
} | |
} | |
extension TextStyle { | |
typealias Name = String | |
enum Emphasis { | |
case none | |
case bold | |
case italic | |
var symbolicTraits: UIFontDescriptor.SymbolicTraits? { | |
switch self { | |
case .none: | |
return nil | |
case .bold: | |
return .traitBold | |
case .italic: | |
return .traitItalic | |
} | |
} | |
} | |
} | |
extension TextStyle.Name { | |
static let montserrat: TextStyle.Name = "Montserrat-Regular" | |
static let ptSans: TextStyle.Name = "PTSans-Regular" | |
} | |
extension TextStyle { | |
// MARK: - Headings | |
static let h1 = TextStyle(size: 24, emphasis: .bold, name: .montserrat) | |
static let h2 = TextStyle(size: 20, emphasis: .bold, name: .montserrat) | |
static let h3 = TextStyle(size: 18, emphasis: .bold, name: .montserrat) | |
static let h4 = TextStyle(size: 16, emphasis: .bold, name: .montserrat) | |
static let h5 = TextStyle(size: 14, emphasis: .bold, name: .montserrat) | |
static let h6 = TextStyle(size: 13, emphasis: .bold, name: .montserrat) | |
// MARK: - Text buttons | |
static func buttonLarge(emphasis: Emphasis) -> TextStyle { | |
TextStyle(size: 16, emphasis: emphasis) | |
} | |
static func buttonSmall(emphasis: Emphasis) -> TextStyle { | |
TextStyle(size: 14, emphasis: emphasis) | |
} | |
// MARK: - Texts | |
static let text18 = TextStyle(size: 18) | |
static let text16 = TextStyle(size: 16) | |
static let text13 = TextStyle(size: 13) | |
static let text14 = TextStyle(size: 14) | |
// MARK: - Text highlights | |
static let textHighlight24 = TextStyle(size: 24, emphasis: .bold) | |
static let textHighlight20 = TextStyle(size: 20, emphasis: .bold) | |
} |
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
protocol TextStyleAdjustable: UIContentSizeCategoryAdjusting { | |
func setFont(_ font: UIFont) | |
func apply(textStyle: TextStyle) -> Self | |
} | |
extension TextStyleAdjustable { | |
@discardableResult | |
func apply(textStyle: TextStyle) -> Self { | |
setFont(.scaledFont(style: textStyle)) | |
adjustsFontForContentSizeCategory = true | |
return self | |
} | |
} | |
extension UILabel: TextStyleAdjustable { | |
func setFont(_ font: UIFont) { | |
self.font = font | |
} | |
} | |
extension UITextView: TextStyleAdjustable { | |
func setFont(_ font: UIFont) { | |
self.font = font | |
} | |
} | |
extension UITextField: TextStyleAdjustable { | |
func setFont(_ font: UIFont) { | |
self.font = font | |
} | |
} | |
extension UIButton: TextStyleAdjustable { | |
func setFont(_ font: UIFont) { | |
titleLabel?.font = font | |
} | |
public var adjustsFontForContentSizeCategory: Bool { | |
get { | |
titleLabel?.adjustsFontForContentSizeCategory ?? false | |
} | |
set { | |
titleLabel?.adjustsFontForContentSizeCategory = newValue | |
} | |
} | |
} |
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
extension UIFont { | |
/// Returns a font matching `style` | |
static func font(style: <ModuleName>.TextStyle) -> UIFont { | |
var descriptor = UIFontDescriptor(name: style.name, size: style.size) | |
if let symbolicTraits = style.emphasis.symbolicTraits { | |
descriptor = descriptor.withSymbolicTraits(symbolicTraits)! | |
} | |
return UIFont(descriptor: descriptor, size: style.size) | |
} | |
/// Returns a font matching `style` scaled to the current Content Size Category. | |
static func scaledFont(style: <ModuleName>.TextStyle) -> UIFont { | |
UIFontMetrics.default.scaledFont(for: font(style: style)) | |
} | |
} |
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
label.apply(textStyle: .h1) | |
textField.apply(textStyle: .text16) | |
textView.apply(textStyle: .text16) | |
button.apply(textStyle: .buttonLarge(emphasis: .bold)) | |
// or | |
label.font = .scaledFont(style: .h1) | |
label.adjustsFontForContentSizeCategory = true | |
textField.font = .scaledFont(style: .text16) | |
textField.adjustsFontForContentSizeCategory = true | |
textView.font = .scaledFont(style: .text16) | |
textView.adjustsFontForContentSizeCategory = true | |
button.titleLabel?.font = .scaledFont(style: .buttonLarge(emphasis: .bold)) | |
button.titleLabel?.adjustsFontForContentSizeCategory = true |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment