Guest User

Untitled

a guest
Dec 14th, 2017
77
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 6.17 KB | None | 0 0
  1. import UIKit
  2.  
  3. extension UIContentSizeCategory {
  4.  
  5. var multiplier: CGFloat {
  6. switch self {
  7. case .accessibilityExtraExtraExtraLarge: return 23 / 16
  8. case .accessibilityExtraExtraLarge: return 22 / 16
  9. case .accessibilityExtraLarge: return 21 / 16
  10. case .accessibilityLarge: return 20 / 16
  11. case .accessibilityMedium: return 19 / 16
  12. case .extraExtraExtraLarge: return 19 / 16
  13. case .extraExtraLarge: return 18 / 16
  14. case .extraLarge: return 17 / 16
  15. case .large: return 1
  16. case .medium: return 15 / 16
  17. case .small: return 14 / 16
  18. case .extraSmall: return 13 / 16
  19. default: return 1
  20. }
  21. }
  22.  
  23. func preferredContentSize(
  24. _ base: CGFloat,
  25. minSize: CGFloat = 0,
  26. maxSize: CGFloat = CGFloat.greatestFiniteMagnitude
  27. ) -> CGFloat {
  28. let result = base * multiplier
  29. return min(max(result, minSize), maxSize)
  30. }
  31.  
  32. }
  33.  
  34. extension Hashable {
  35. func combineHash<T: Hashable>(with hashableOther: T) -> Int {
  36. let ownHash = self.hashValue
  37. let otherHash = hashableOther.hashValue
  38. return (ownHash << 5) &+ ownHash &+ otherHash
  39. }
  40. }
  41.  
  42. extension UIFont {
  43.  
  44. func addingTraits(traits: UIFontDescriptorSymbolicTraits) -> UIFont {
  45. let newTraits = fontDescriptor.symbolicTraits.union(traits)
  46. guard let descriptor = fontDescriptor.withSymbolicTraits(newTraits)
  47. else { return self }
  48. return UIFont(descriptor: descriptor, size: 0)
  49. }
  50.  
  51. }
  52.  
  53. struct TextStyle: Hashable, Equatable {
  54.  
  55. let name: String
  56. let size: CGFloat
  57. let traits: UIFontDescriptorSymbolicTraits
  58. let minSize: CGFloat
  59. let maxSize: CGFloat
  60.  
  61. init(
  62. name: String = UIFont.systemFont(ofSize: 1).fontName,
  63. size: CGFloat = UIFont.systemFontSize,
  64. traits: UIFontDescriptorSymbolicTraits = [],
  65. minSize: CGFloat = 0,
  66. maxSize: CGFloat = .greatestFiniteMagnitude
  67. ) {
  68. self.name = name
  69. self.size = size
  70. self.traits = traits
  71. self.minSize = minSize
  72. self.maxSize = maxSize
  73.  
  74. self._hashValue = name
  75. .combineHash(with: size)
  76. .combineHash(with: traits.rawValue)
  77. .combineHash(with: minSize)
  78. .combineHash(with: maxSize)
  79. }
  80.  
  81. private let _hashValue: Int
  82. var hashValue: Int {
  83. return _hashValue
  84. }
  85.  
  86. static func == (lhs: TextStyle, rhs: TextStyle) -> Bool {
  87. return lhs.name == rhs.name
  88. && lhs.size == rhs.size
  89. && lhs.traits == rhs.traits
  90. && lhs.minSize == rhs.minSize
  91. && rhs.maxSize == rhs.maxSize
  92. }
  93.  
  94. }
  95.  
  96. struct StyledText: Hashable, Equatable {
  97.  
  98. let text: String
  99. let style: TextStyle
  100. let attributes: [NSAttributedStringKey: Any]
  101.  
  102. init(
  103. text: String,
  104. style: TextStyle = TextStyle(),
  105. attributes: [NSAttributedStringKey: Any] = [:]
  106. ) {
  107. self.text = text
  108. self.style = style
  109. self.attributes = attributes
  110. }
  111.  
  112. func font(size: CGFloat) -> UIFont {
  113. guard let font = UIFont(name: style.name, size: size) else {
  114. return UIFont.systemFont(ofSize: size)
  115. }
  116. return font.addingTraits(traits: style.traits)
  117. }
  118.  
  119. func render(contentSizeCategory: UIContentSizeCategory) -> NSAttributedString {
  120. var attributes = self.attributes
  121. attributes[.font] = font(size: contentSizeCategory.preferredContentSize(style.size))
  122. return NSAttributedString(string: text, attributes: attributes)
  123. }
  124.  
  125. var hashValue: Int {
  126. return text
  127. .combineHash(with: style)
  128. }
  129.  
  130. static func == (lhs: StyledText, rhs: StyledText) -> Bool {
  131. return lhs.text == rhs.text
  132. && lhs.style == rhs.style
  133. }
  134.  
  135. }
  136.  
  137. struct StyledTextBuilder {
  138.  
  139. let styledTexts: [StyledText]
  140.  
  141. func adding(styledTexts: [StyledText]) -> StyledTextBuilder {
  142. return StyledTextBuilder(styledTexts: self.styledTexts + styledTexts)
  143. }
  144.  
  145. func adding(styledText: StyledText) -> StyledTextBuilder {
  146. return adding(styledTexts: [styledText])
  147. }
  148.  
  149. func adding(text: String) -> StyledTextBuilder {
  150. guard let tip = styledTexts.last else { return self }
  151. return adding(styledText: StyledText(text: text, style: tip.style, attributes: tip.attributes))
  152. }
  153.  
  154. func adding(text: String, attributes: [NSAttributedStringKey: Any]) -> StyledTextBuilder {
  155. guard let tip = styledTexts.last else { return self }
  156. return adding(styledText: StyledText(text: text, style: tip.style, attributes: attributes))
  157. }
  158.  
  159. func adding(
  160. text: String,
  161. traits: UIFontDescriptorSymbolicTraits? = nil,
  162. attributes: [NSAttributedStringKey: Any]? = nil
  163. ) -> StyledTextBuilder {
  164. guard let tip = styledTexts.last else { return self }
  165.  
  166. var nextAttributes = tip.attributes
  167. if let attributes = attributes {
  168. for (k, v) in attributes {
  169. nextAttributes[k] = v
  170. }
  171. }
  172.  
  173. let nextStyle: TextStyle
  174. if let traits = traits {
  175. nextStyle = TextStyle(
  176. name: tip.style.name,
  177. size: tip.style.size,
  178. traits: tip.style.traits.union(traits),
  179. minSize: tip.style.minSize,
  180. maxSize: tip.style.maxSize
  181. )
  182. } else {
  183. nextStyle = tip.style
  184. }
  185.  
  186. return adding(
  187. styledText: StyledText(
  188. text: text,
  189. style: nextStyle,
  190. attributes: nextAttributes
  191. )
  192. )
  193. }
  194.  
  195. func render(contentSizeCategory: UIContentSizeCategory) -> NSAttributedString {
  196. let result = NSMutableAttributedString()
  197. styledTexts.forEach { result.append($0.render(contentSizeCategory: contentSizeCategory)) }
  198. return result
  199. }
  200.  
  201. }
  202.  
  203. let seed = StyledText(text: "foo", attributes: [.foregroundColor: UIColor.white])
  204. let builder = StyledTextBuilder(styledTexts: [seed])
  205. .adding(text: " bar", traits: [.traitBold, .traitItalic])
  206. let attr = builder.render(contentSizeCategory: .medium)
  207.  
  208. import PlaygroundSupport
  209.  
  210. let someView = UILabel()
  211. someView.attributedText = attr
  212. someView.sizeToFit()
  213. PlaygroundPage.current.liveView = someView
Add Comment
Please, Sign In to add comment