Last active
June 7, 2019 00:19
-
-
Save stevethomp/e71e4d1be87b3d2361edd6807906741b 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
This file contains hidden or 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
import UIKit | |
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: Notification.Name.UIContentSizeCategoryDidChange, | |
object: nil, | |
queue: OperationQueue.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 { | |
var textStyle: UIFontTextStyle? { | |
guard let fontAttribute = fontAttributes["NSCTFontUIUsageAttribute"] 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 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