Advertisement
Guest User

Untitled

a guest
Oct 22nd, 2019
99
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 10.75 KB | None | 0 0
  1. import UIKit
  2.  
  3. protocol LayoutAnchor {
  4.  
  5. func constraint(equalTo anchor: Self, constant: CGFloat) -> NSLayoutConstraint
  6. func constraint(greaterThanOrEqualTo anchor: Self, constant: CGFloat) -> NSLayoutConstraint
  7. func constraint(lessThanOrEqualTo anchor: Self, constant: CGFloat) -> NSLayoutConstraint
  8. }
  9.  
  10. protocol LayoutDimension: LayoutAnchor {
  11.  
  12. func constraint(equalToConstant c: CGFloat) -> NSLayoutConstraint
  13. func constraint(greaterThanOrEqualToConstant c: CGFloat) -> NSLayoutConstraint
  14. func constraint(lessThanOrEqualToConstant c: CGFloat) -> NSLayoutConstraint
  15.  
  16. func constraint(equalTo anchor: Self, multiplier m: CGFloat) -> NSLayoutConstraint
  17. }
  18.  
  19. extension NSLayoutAnchor: LayoutAnchor {}
  20. extension NSLayoutDimension: LayoutDimension {}
  21.  
  22. class LayoutProperty<Anchor: LayoutAnchor> {
  23.  
  24. fileprivate let anchor: Anchor
  25. fileprivate let kind: Kind
  26.  
  27. enum Kind { case leading, trailing, top, bottom, centerX, centerY, width, height }
  28.  
  29. init(anchor: Anchor, kind: Kind) {
  30. self.anchor = anchor
  31. self.kind = kind
  32. }
  33. }
  34.  
  35. class LayoutAttribute<Dimension: LayoutDimension>: LayoutProperty<Dimension> {
  36.  
  37. fileprivate let dimension: Dimension
  38.  
  39. init(dimension: Dimension, kind: Kind) {
  40. self.dimension = dimension
  41.  
  42. super.init(anchor: dimension, kind: kind)
  43. }
  44. }
  45.  
  46. final class LayoutProxy {
  47.  
  48. lazy var leading = property(with: view.leadingAnchor, kind: .leading)
  49. lazy var trailing = property(with: view.trailingAnchor, kind: .trailing)
  50. lazy var top = property(with: view.topAnchor, kind: .top)
  51. lazy var bottom = property(with: view.bottomAnchor, kind: .bottom)
  52. lazy var centerX = property(with: view.centerXAnchor, kind: .centerX)
  53. lazy var centerY = property(with: view.centerYAnchor, kind: .centerY)
  54. lazy var width = attribute(with: view.widthAnchor, kind: .width)
  55. lazy var height = attribute(with: view.heightAnchor, kind: .height)
  56.  
  57. private let view: UIView
  58.  
  59. fileprivate init(view: UIView) {
  60. self.view = view
  61. }
  62.  
  63. private func property<A: LayoutAnchor>(with anchor: A, kind: LayoutProperty<A>.Kind) -> LayoutProperty<A> {
  64. return LayoutProperty(anchor: anchor, kind: kind)
  65. }
  66.  
  67. private func attribute<D: LayoutDimension>(with dimension: D, kind: LayoutProperty<D>.Kind) -> LayoutAttribute<D> {
  68. return LayoutAttribute(dimension: dimension, kind: kind)
  69. }
  70. }
  71.  
  72. extension LayoutAttribute {
  73.  
  74. @discardableResult
  75. func equal(to constant: CGFloat, priority: UILayoutPriority? = nil) -> NSLayoutConstraint {
  76. let constraint = dimension.constraint(equalToConstant: constant)
  77. (constraint.firstItem as? UIView)?.layout.update(constraint: constraint, kind: kind)
  78. if let priority = priority {
  79. constraint.priority = priority
  80. }
  81. constraint.isActive = true
  82. return constraint
  83. }
  84.  
  85. @discardableResult
  86. func equal(to otherDimension: Dimension, multiplier: CGFloat, priority: UILayoutPriority? = nil) -> NSLayoutConstraint {
  87. let constraint = dimension.constraint(equalTo: otherDimension, multiplier: multiplier)
  88. (constraint.firstItem as? UIView)?.layout.update(constraint: constraint, kind: kind)
  89. if let priority = priority {
  90. constraint.priority = priority
  91. }
  92. constraint.isActive = true
  93. return constraint
  94. }
  95.  
  96. @discardableResult
  97. func greaterThanOrEqual(to constant: CGFloat, priority: UILayoutPriority? = nil) -> NSLayoutConstraint {
  98. let constraint = dimension.constraint(greaterThanOrEqualToConstant: constant)
  99. (constraint.firstItem as? UIView)?.layout.update(constraint: constraint, kind: kind)
  100. if let priority = priority {
  101. constraint.priority = priority
  102. }
  103. constraint.isActive = true
  104. return constraint
  105. }
  106.  
  107. @discardableResult
  108. func lessThanOrEqual(to constant: CGFloat, priority: UILayoutPriority? = nil) -> NSLayoutConstraint {
  109. let constraint = dimension.constraint(lessThanOrEqualToConstant: constant)
  110. (constraint.firstItem as? UIView)?.layout.update(constraint: constraint, kind: kind)
  111. if let priority = priority {
  112. constraint.priority = priority
  113. }
  114. constraint.isActive = true
  115. return constraint
  116. }
  117. }
  118.  
  119. extension LayoutProperty {
  120.  
  121. @discardableResult
  122. func equal(to otherAnchor: Anchor, offsetBy constant: CGFloat = 0, priority: UILayoutPriority? = nil) -> NSLayoutConstraint {
  123. let constraint = anchor.constraint(equalTo: otherAnchor, constant: constant)
  124. (constraint.firstItem as? UIView)?.layout.update(constraint: constraint, kind: kind)
  125. if let priority = priority {
  126. constraint.priority = priority
  127. }
  128. constraint.isActive = true
  129. return constraint
  130. }
  131.  
  132. @discardableResult
  133. func greaterThanOrEqual(to otherAnchor: Anchor, offsetBy constant: CGFloat = 0, priority: UILayoutPriority? = nil) -> NSLayoutConstraint {
  134. let constraint = anchor.constraint(greaterThanOrEqualTo: otherAnchor, constant: constant)
  135. (constraint.firstItem as? UIView)?.layout.update(constraint: constraint, kind: kind)
  136. if let priority = priority {
  137. constraint.priority = priority
  138. }
  139. constraint.isActive = true
  140. return constraint
  141. }
  142.  
  143. @discardableResult
  144. func lessThanOrEqual(to otherAnchor: Anchor, offsetBy constant: CGFloat = 0, priority: UILayoutPriority? = nil) -> NSLayoutConstraint {
  145. let constraint = anchor.constraint(lessThanOrEqualTo: otherAnchor, constant: constant)
  146. (constraint.firstItem as? UIView)?.layout.update(constraint: constraint, kind: kind)
  147. if let priority = priority {
  148. constraint.priority = priority
  149. }
  150. constraint.isActive = true
  151. return constraint
  152. }
  153. }
  154.  
  155. extension UIView {
  156.  
  157. func layout(using closure: (LayoutProxy) -> Void) {
  158. translatesAutoresizingMaskIntoConstraints = false
  159. closure(LayoutProxy(view: self))
  160. }
  161.  
  162. func layout(in superview: UIView, with insets: UIEdgeInsets = .zero) {
  163. superview.addSubview(self)
  164. layout { proxy in
  165. proxy.bottom == superview.bottomAnchor - insets.bottom
  166. proxy.top == superview.topAnchor + insets.top
  167. proxy.leading == superview.leadingAnchor + insets.left
  168. proxy.trailing == superview.trailingAnchor - insets.right
  169. }
  170. }
  171. }
  172.  
  173. //swiftlint:disable large_tuple
  174.  
  175. func +<A: LayoutAnchor>(lhs: A, rhs: CGFloat) -> (A, CGFloat) {
  176. return (lhs, rhs)
  177. }
  178.  
  179. func -<A: LayoutAnchor>(lhs: A, rhs: CGFloat) -> (A, CGFloat) {
  180. return (lhs, -rhs)
  181. }
  182.  
  183. @discardableResult
  184. func ==<A: LayoutAnchor>(lhs: LayoutProperty<A>, rhs: (A, CGFloat)) -> NSLayoutConstraint {
  185. return lhs.equal(to: rhs.0, offsetBy: rhs.1)
  186. }
  187.  
  188. @discardableResult
  189. func ==<A: LayoutAnchor>(lhs: LayoutProperty<A>, rhs: ((A, CGFloat), UILayoutPriority)) -> NSLayoutConstraint {
  190. return lhs.equal(to: rhs.0.0, offsetBy: rhs.0.1, priority: rhs.1)
  191. }
  192.  
  193. @discardableResult
  194. func ==<A: LayoutAnchor>(lhs: LayoutProperty<A>, rhs: (A, UILayoutPriority)) -> NSLayoutConstraint {
  195. return lhs.equal(to: rhs.0, priority: rhs.1)
  196. }
  197.  
  198. @discardableResult
  199. func ==<A: LayoutAnchor>(lhs: LayoutProperty<A>, rhs: A) -> NSLayoutConstraint {
  200. return lhs.equal(to: rhs)
  201. }
  202.  
  203. @discardableResult
  204. func >=<A: LayoutAnchor>(lhs: LayoutProperty<A>, rhs: (A, CGFloat)) -> NSLayoutConstraint {
  205. return lhs.greaterThanOrEqual(to: rhs.0, offsetBy: rhs.1)
  206. }
  207.  
  208. @discardableResult
  209. func >=<A: LayoutAnchor>(lhs: LayoutProperty<A>, rhs: A) -> NSLayoutConstraint {
  210. return lhs.greaterThanOrEqual(to: rhs)
  211. }
  212.  
  213. @discardableResult
  214. func <=<A: LayoutAnchor>(lhs: LayoutProperty<A>, rhs: (A, CGFloat)) -> NSLayoutConstraint {
  215. return lhs.lessThanOrEqual(to: rhs.0, offsetBy: rhs.1)
  216. }
  217.  
  218. @discardableResult
  219. func <=<A: LayoutAnchor>(lhs: LayoutProperty<A>, rhs: A) -> NSLayoutConstraint {
  220. return lhs.lessThanOrEqual(to: rhs)
  221. }
  222.  
  223. @discardableResult
  224. func <=<D: LayoutDimension>(lhs: LayoutAttribute<D>, rhs: CGFloat) -> NSLayoutConstraint {
  225. return lhs.lessThanOrEqual(to: rhs)
  226. }
  227.  
  228. @discardableResult
  229. func ==<D: LayoutDimension>(lhs: LayoutAttribute<D>, rhs: CGFloat) -> NSLayoutConstraint {
  230. return lhs.equal(to: rhs)
  231. }
  232.  
  233. @discardableResult
  234. func ==<D: LayoutDimension>(lhs: LayoutAttribute<D>, rhs: (CGFloat, UILayoutPriority)) -> NSLayoutConstraint {
  235. return lhs.equal(to: rhs.0, priority: rhs.1)
  236. }
  237.  
  238. @discardableResult
  239. func ==<D: LayoutDimension>(lhs: LayoutAttribute<D>, rhs: LayoutAttribute<D>) -> NSLayoutConstraint {
  240. return lhs.equal(to: rhs.dimension)
  241. }
  242.  
  243. @discardableResult
  244. func *=<D: LayoutDimension>(lhs: LayoutAttribute<D>, rhs: (D, CGFloat)) -> NSLayoutConstraint {
  245. return lhs.equal(to: rhs.0, multiplier: rhs.1)
  246. }
  247.  
  248. @discardableResult
  249. func *=<D: LayoutDimension>(lhs: LayoutAttribute<D>, rhs: (LayoutAttribute<D>, CGFloat, UILayoutPriority)) -> NSLayoutConstraint {
  250. return lhs.equal(to: rhs.0.dimension, multiplier: rhs.1, priority: rhs.2)
  251. }
  252.  
  253. @discardableResult
  254. func >=<D: LayoutDimension>(lhs: LayoutAttribute<D>, rhs: CGFloat) -> NSLayoutConstraint {
  255. return lhs.greaterThanOrEqual(to: rhs)
  256. }
  257.  
  258. //swiftlint:enable large_tuple
  259.  
  260. extension UIView {
  261.  
  262. private struct AssociatedKeys {
  263. static var layout = "layout"
  264. }
  265.  
  266. var layout: Layout {
  267. get {
  268. var layout: Layout!
  269. let lookup = objc_getAssociatedObject(self, &AssociatedKeys.layout) as? Layout
  270. if let lookup = lookup {
  271. layout = lookup
  272. } else {
  273. let newLayout = Layout()
  274. self.layout = newLayout
  275. layout = newLayout
  276. }
  277. return layout
  278. }
  279. set {
  280. objc_setAssociatedObject(self, &AssociatedKeys.layout, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
  281. }
  282. }
  283.  
  284. func addSubviews(_ views: UIView...) {
  285. views.forEach { addSubview($0) }
  286. }
  287. }
  288.  
  289. final class Layout: NSObject {
  290.  
  291. weak var top: NSLayoutConstraint?
  292. weak var bottom: NSLayoutConstraint?
  293. weak var leading: NSLayoutConstraint?
  294. weak var trailing: NSLayoutConstraint?
  295. weak var centerX: NSLayoutConstraint?
  296. weak var centerY: NSLayoutConstraint?
  297. weak var width: NSLayoutConstraint?
  298. weak var height: NSLayoutConstraint?
  299.  
  300. fileprivate func update<A: LayoutAnchor>(constraint: NSLayoutConstraint, kind: LayoutProperty<A>.Kind) {
  301. switch kind {
  302. case .top: top = constraint
  303. case .bottom: bottom = constraint
  304. case .leading: leading = constraint
  305. case .trailing: trailing = constraint
  306. case .centerX: centerX = constraint
  307. case .centerY: centerY = constraint
  308. case .width: width = constraint
  309. case .height: height = constraint
  310. }
  311. }
  312. }
  313.  
  314. extension UILayoutPriority {
  315.  
  316. static let lowered = UILayoutPriority(999)
  317. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement