Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import UIKit
- extension UIContentSizeCategory {
- var multiplier: CGFloat {
- switch self {
- case .accessibilityExtraExtraExtraLarge: return 23 / 16
- case .accessibilityExtraExtraLarge: return 22 / 16
- case .accessibilityExtraLarge: return 21 / 16
- case .accessibilityLarge: return 20 / 16
- case .accessibilityMedium: return 19 / 16
- case .extraExtraExtraLarge: return 19 / 16
- case .extraExtraLarge: return 18 / 16
- case .extraLarge: return 17 / 16
- case .large: return 1
- case .medium: return 15 / 16
- case .small: return 14 / 16
- case .extraSmall: return 13 / 16
- default: return 1
- }
- }
- func preferredContentSize(
- _ base: CGFloat,
- minSize: CGFloat = 0,
- maxSize: CGFloat = CGFloat.greatestFiniteMagnitude
- ) -> CGFloat {
- let result = base * multiplier
- return min(max(result, minSize), maxSize)
- }
- }
- extension Hashable {
- func combineHash<T: Hashable>(with hashableOther: T) -> Int {
- let ownHash = self.hashValue
- let otherHash = hashableOther.hashValue
- return (ownHash << 5) &+ ownHash &+ otherHash
- }
- }
- extension UIFont {
- func addingTraits(traits: UIFontDescriptorSymbolicTraits) -> UIFont {
- let newTraits = fontDescriptor.symbolicTraits.union(traits)
- guard let descriptor = fontDescriptor.withSymbolicTraits(newTraits)
- else { return self }
- return UIFont(descriptor: descriptor, size: 0)
- }
- }
- struct TextStyle: Hashable, Equatable {
- let name: String
- let size: CGFloat
- let traits: UIFontDescriptorSymbolicTraits
- let minSize: CGFloat
- let maxSize: CGFloat
- init(
- name: String = UIFont.systemFont(ofSize: 1).fontName,
- size: CGFloat = UIFont.systemFontSize,
- traits: UIFontDescriptorSymbolicTraits = [],
- minSize: CGFloat = 0,
- maxSize: CGFloat = .greatestFiniteMagnitude
- ) {
- self.name = name
- self.size = size
- self.traits = traits
- self.minSize = minSize
- self.maxSize = maxSize
- self._hashValue = name
- .combineHash(with: size)
- .combineHash(with: traits.rawValue)
- .combineHash(with: minSize)
- .combineHash(with: maxSize)
- }
- private let _hashValue: Int
- var hashValue: Int {
- return _hashValue
- }
- static func == (lhs: TextStyle, rhs: TextStyle) -> Bool {
- return lhs.name == rhs.name
- && lhs.size == rhs.size
- && lhs.traits == rhs.traits
- && lhs.minSize == rhs.minSize
- && rhs.maxSize == rhs.maxSize
- }
- }
- struct StyledText: Hashable, Equatable {
- let text: String
- let style: TextStyle
- let attributes: [NSAttributedStringKey: Any]
- init(
- text: String,
- style: TextStyle = TextStyle(),
- attributes: [NSAttributedStringKey: Any] = [:]
- ) {
- self.text = text
- self.style = style
- self.attributes = attributes
- }
- func font(size: CGFloat) -> UIFont {
- guard let font = UIFont(name: style.name, size: size) else {
- return UIFont.systemFont(ofSize: size)
- }
- return font.addingTraits(traits: style.traits)
- }
- func render(contentSizeCategory: UIContentSizeCategory) -> NSAttributedString {
- var attributes = self.attributes
- attributes[.font] = font(size: contentSizeCategory.preferredContentSize(style.size))
- return NSAttributedString(string: text, attributes: attributes)
- }
- var hashValue: Int {
- return text
- .combineHash(with: style)
- }
- static func == (lhs: StyledText, rhs: StyledText) -> Bool {
- return lhs.text == rhs.text
- && lhs.style == rhs.style
- }
- }
- struct StyledTextBuilder {
- let styledTexts: [StyledText]
- func adding(styledTexts: [StyledText]) -> StyledTextBuilder {
- return StyledTextBuilder(styledTexts: self.styledTexts + styledTexts)
- }
- func adding(styledText: StyledText) -> StyledTextBuilder {
- return adding(styledTexts: [styledText])
- }
- func adding(text: String) -> StyledTextBuilder {
- guard let tip = styledTexts.last else { return self }
- return adding(styledText: StyledText(text: text, style: tip.style, attributes: tip.attributes))
- }
- func adding(text: String, attributes: [NSAttributedStringKey: Any]) -> StyledTextBuilder {
- guard let tip = styledTexts.last else { return self }
- return adding(styledText: StyledText(text: text, style: tip.style, attributes: attributes))
- }
- func adding(
- text: String,
- traits: UIFontDescriptorSymbolicTraits? = nil,
- attributes: [NSAttributedStringKey: Any]? = nil
- ) -> StyledTextBuilder {
- guard let tip = styledTexts.last else { return self }
- var nextAttributes = tip.attributes
- if let attributes = attributes {
- for (k, v) in attributes {
- nextAttributes[k] = v
- }
- }
- let nextStyle: TextStyle
- if let traits = traits {
- nextStyle = TextStyle(
- name: tip.style.name,
- size: tip.style.size,
- traits: tip.style.traits.union(traits),
- minSize: tip.style.minSize,
- maxSize: tip.style.maxSize
- )
- } else {
- nextStyle = tip.style
- }
- return adding(
- styledText: StyledText(
- text: text,
- style: nextStyle,
- attributes: nextAttributes
- )
- )
- }
- func render(contentSizeCategory: UIContentSizeCategory) -> NSAttributedString {
- let result = NSMutableAttributedString()
- styledTexts.forEach { result.append($0.render(contentSizeCategory: contentSizeCategory)) }
- return result
- }
- }
- let seed = StyledText(text: "foo", attributes: [.foregroundColor: UIColor.white])
- let builder = StyledTextBuilder(styledTexts: [seed])
- .adding(text: " bar", traits: [.traitBold, .traitItalic])
- let attr = builder.render(contentSizeCategory: .medium)
- import PlaygroundSupport
- let someView = UILabel()
- someView.attributedText = attr
- someView.sizeToFit()
- PlaygroundPage.current.liveView = someView
Add Comment
Please, Sign In to add comment