Skip to content

Instantly share code, notes, and snippets.

@hvsw
Forked from stevethomp/DynamicTypeResponsive.swift
Created October 15, 2018 19:09
Show Gist options
  • Save hvsw/d6027be8dd73f4dc71bbfb374ba8b2f8 to your computer and use it in GitHub Desktop.
Save hvsw/d6027be8dd73f4dc71bbfb374ba8b2f8 to your computer and use it in GitHub Desktop.
Adds a simple way to register a view for automatic dynamic type updating by setting respondsToDynamicTypeChanges = true. Really only useful on iOS < 10
import Foundation
import ObjectiveC
// MARK: - DynamicTypeResponsive
protocol DynamicTypeResponsive {
var respondsToDynamicTypeChanges: Bool { get set }
}
extension UILabel: DynamicTypeResponsive {
var respondsToDynamicTypeChanges: Bool {
set {
if #available(iOS 10.0, *) {
self.adjustsFontForContentSizeCategory = newValue
} else {
let handler: NotificationHandler = { [weak self] _ in
if let strongSelf = self,
let style = strongSelf.font.textStyle {
strongSelf.font = .preferredFont(forTextStyle: style)
strongSelf.invalidateIntrinsicContentSize()
}
}
self.setContentSizeDidChangeHandler(newValue ? handler : nil)
}
}
get {
if #available(iOS 10.0, *) {
return self.adjustsFontForContentSizeCategory
} else {
return self.contentSizeNotificationObserver != nil
}
}
}
}
extension UITextView: DynamicTypeResponsive {
var respondsToDynamicTypeChanges: Bool {
set {
if #available(iOS 10.0, *) {
self.adjustsFontForContentSizeCategory = newValue
} else {
let handler: NotificationHandler = { [weak self] _ in
if let strongSelf = self,
let style = strongSelf.font?.textStyle {
strongSelf.font = .preferredFont(forTextStyle: style)
strongSelf.invalidateIntrinsicContentSize()
}
}
self.setContentSizeDidChangeHandler(newValue ? handler : nil)
}
}
get {
if #available(iOS 10.0, *) {
return self.adjustsFontForContentSizeCategory
} else {
return self.contentSizeNotificationObserver != nil
}
}
}
}
extension UITextField: DynamicTypeResponsive {
var respondsToDynamicTypeChanges: Bool {
set {
if #available(iOS 10.0, *) {
self.adjustsFontForContentSizeCategory = newValue
} else {
let handler: NotificationHandler = { [weak self] _ in
if let strongSelf = self,
let style = strongSelf.font?.textStyle {
strongSelf.font = .preferredFont(forTextStyle: style)
strongSelf.invalidateIntrinsicContentSize()
}
}
self.setContentSizeDidChangeHandler(newValue ? handler : nil)
}
}
get {
if #available(iOS 10.0, *) {
return self.adjustsFontForContentSizeCategory
} else {
return self.contentSizeNotificationObserver != nil
}
}
}
}
extension UIButton: DynamicTypeResponsive {
var respondsToDynamicTypeChanges: Bool {
set {
self.titleLabel?.respondsToDynamicTypeChanges = newValue
}
get {
return self.titleLabel?.respondsToDynamicTypeChanges ?? false
}
}
}
// MARK: - Notification Handling for UIContentSizeCategoryDidChange
typealias NotificationHandler = (Notification) -> Void
var NotificationObserverKey: String = "NotificationObserverKey"
protocol DynamicTypeAdjusting: class {
func setContentSizeDidChangeHandler(_ handler: NotificationHandler?)
var contentSizeNotificationObserver: NSObjectProtocol? { get set }
}
extension DynamicTypeAdjusting where Self: NSObject {
func setContentSizeDidChangeHandler(_ handler: NotificationHandler?) {
if let handler = handler {
let observer = NotificationCenter.default.addObserver(forName: .UIContentSizeCategoryDidChange,
object: nil,
queue: .main,
using: handler)
self.contentSizeNotificationObserver = observer
} else {
self.contentSizeNotificationObserver = nil
}
}
var contentSizeNotificationObserver: NSObjectProtocol? {
set {
withUnsafePointer(to: &NotificationObserverKey) { (p) -> Void in
objc_setAssociatedObject(self, p, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
get {
return withUnsafePointer(to: &NotificationObserverKey) { (p) -> NSObjectProtocol? in
return objc_getAssociatedObject(self, p) as? NSObjectProtocol
}
}
}
}
extension UILabel: DynamicTypeAdjusting { }
extension UITextField: DynamicTypeAdjusting { }
extension UITextView: DynamicTypeAdjusting { }
// MARK: - UIFont Helpers
extension UIFont {
var textStyle: UIFontTextStyle? {
return self.fontDescriptor.textStyle
}
}
extension UIFontDescriptor.AttributeName {
static let nsctFontUIUsage = UIFontDescriptor.AttributeName(rawValue: "NSCTFontUIUsageAttribute")
}
extension UIFontDescriptor {
var textStyle: UIFontTextStyle? {
guard let fontAttribute = fontAttributes[.nsctFontUIUsage] as? String else {
return nil
}
switch fontAttribute {
case "UICTFontTextStyleHeadline", "AppleSystemUIHeadline":
return .headline
case "UICTFontTextStyleSubhead":
return .subheadline
case "UICTFontTextStyleBody":
return .body
case "UICTFontTextStyleFootnote":
return .footnote
case "UICTFontTextStyleCaption1":
return .caption1
case "UICTFontTextStyleCaption2":
return .caption2
case "UICTFontTextStyleCallout":
return .callout
case "UICTFontTextStyleTitle1":
return .title1
case "UICTFontTextStyleTitle2":
return .title2
case "UICTFontTextStyleTitle3":
return .title3
default:
fatalError("Style '\(fontAttribute)' not handled!") // Will need to support new styles added in iOS 11
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment