GoldenJoe

TinyConstraints+Extras

Apr 15th, 2017
359
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Swift 7.93 KB | None | 0 0
  1. //
  2. //  TinyConstraints+Extras.swift
  3. //  WeThePeople
  4. //
  5. //  Created by Joseph Falcone on 4/15/17.
  6. //  Copyright © 2017 Joseph Falcone. All rights reserved.
  7. //
  8.  
  9. import Foundation
  10. import TinyConstraints
  11.  
  12. public struct TextLayoutOptions : OptionSet
  13. {
  14.     public let rawValue : Int
  15.     public init(rawValue:Int){ self.rawValue = rawValue}
  16.    
  17.     public static let IgnoreAscender  = TextLayoutOptions(rawValue:1)
  18.     public static let IgnoreDescender = TextLayoutOptions(rawValue:2)
  19. }
  20.  
  21. public struct ViewEdge : OptionSet, Hashable
  22. {
  23.     public let rawValue: Int
  24.     public init(rawValue:Int){ self.rawValue = rawValue}
  25.     public var hashValue: Int {
  26.         return self.rawValue
  27.     }
  28.    
  29.     static let top      = ViewEdge(rawValue: 1 << 0)
  30.     static let right    = ViewEdge(rawValue: 1 << 1) // try not to use right and left
  31.     static let bottom   = ViewEdge(rawValue: 1 << 2)
  32.     static let left     = ViewEdge(rawValue: 1 << 3) // leading and trailing are better for text
  33.    
  34.     static let leading  = ViewEdge(rawValue: 1 << 4) // left for western users
  35.     static let trailing = ViewEdge(rawValue: 1 << 5) // right for western users
  36.    
  37.     static func test(){}
  38. }
  39.  
  40. public extension Constrainable
  41. {
  42.     @discardableResult
  43.     public func edges(to view: Constrainable, excludingEdges: ViewEdge = [], insets: EdgeInsets = .zero, priority: ConstraintPriority = .required, isActive: Bool = true) -> Constraints {
  44.        
  45.         var constraints = [NSLayoutConstraint]()
  46.        
  47.         if !excludingEdges.contains(.top){
  48.             constraints.append(topAnchor.constraint(equalTo: view.topAnchor, constant: insets.top).with(priority))
  49.         }
  50.         if !excludingEdges.contains(.leading) || !excludingEdges.contains(.left){
  51.             constraints.append(leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: insets.left).with(priority))
  52.         }
  53.         if !excludingEdges.contains(.bottom){
  54.             constraints.append(bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: insets.bottom).with(priority))
  55.         }
  56.         if !excludingEdges.contains(.trailing) || !excludingEdges.contains(.right){
  57.             constraints.append(trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: insets.right).with(priority))
  58.         }
  59.        
  60.         if isActive {
  61.             Constraint.activate(constraints)
  62.         }
  63.        
  64.         return constraints
  65.     }
  66.    
  67.     // These autolayout extensions are designed for classes that display text. This returns the display font if the class type is valid.
  68.     // UILabel
  69.     // UITextField
  70.     private func getDisplayFont() -> UIFont?
  71.     {
  72.         if let v = self as? UILabel     {return v.font}
  73.         if let v = self as? UITextField {return v.font}
  74.         //if let v = self as? UITextView  {return v.font} UITextView is weird...the top align represents where the text gets cut off...
  75.         return nil
  76.     }
  77.     // NOTE: The font must be set before this returns anything of value
  78.     // NOTE: All values are positive since they are insets, even though descender is normally negative
  79.     private func typographyInsets() -> UIEdgeInsets
  80.     {
  81.         guard let font = self.getDisplayFont() else {
  82.             return UIEdgeInsets.zero
  83.         }
  84.         return UIEdgeInsets(top: (font.ascender - font.capHeight), left: 0, bottom: abs(font.descender), right: 0)
  85.     }
  86.     private func getOffsetModifier(to: Constrainable, edge: ViewEdge, toEdge: ViewEdge, textOptions: TextLayoutOptions = []) -> CGFloat
  87.     {
  88.         // Tired of ascenders/descenders being ignored.
  89.         // Let's make it easier to space these things out.
  90.         var typographyOffset : CGFloat = 0
  91.        
  92.         let myTI = self.typographyInsets()
  93.         let toTI = to.typographyInsets()
  94.        
  95.         if edge == toEdge {
  96.             // top to top, bottom to bottom
  97.             if edge == .top {
  98.                 typographyOffset = toTI.top - myTI.top
  99.             }
  100.             if edge == .bottom {
  101.                 typographyOffset = myTI.bottom - toTI.bottom
  102.             }
  103.         }
  104.        
  105.         if edge == .top && toEdge == .bottom {
  106.             typographyOffset = -(myTI.top + toTI.bottom)
  107.         }
  108.        
  109.         if edge == .bottom && toEdge == .top {
  110.             typographyOffset = myTI.bottom + toTI.top
  111.         }
  112.        
  113.         return typographyOffset;
  114.     }
  115.    
  116.     @discardableResult
  117.     public func topToBottom(of view: Constrainable, offset: CGFloat = 0, relation: ConstraintRelation = .equal, priority: ConstraintPriority = .required, textOptions: TextLayoutOptions = [], isActive: Bool = true) -> Constraint {
  118.         return top(to: view, view.bottomAnchor, offset: offset, relation: relation, priority: priority, textOptions: textOptions, isActive: isActive)
  119.     }
  120.    
  121.     @discardableResult
  122.     public func top(to view: Constrainable, _ anchor: NSLayoutYAxisAnchor? = nil, offset: CGFloat = 0, relation: ConstraintRelation = .equal, priority: ConstraintPriority = .required, textOptions: TextLayoutOptions = [], isActive: Bool = true) -> Constraint {
  123.         let toEdge = (anchor != nil) ? ViewEdge.bottom : ViewEdge.top
  124.         let offsetMod = self.getOffsetModifier(to: view, edge: ViewEdge.top, toEdge: toEdge, textOptions: textOptions)
  125.        
  126.         switch relation {
  127.         case .equal:            return topAnchor.constraint(equalTo:                anchor ?? view.topAnchor, constant: offset+offsetMod).with(priority).set(active: isActive)
  128.         case .equalOrLess:      return topAnchor.constraint(lessThanOrEqualTo:      anchor ?? view.topAnchor, constant: offset+offsetMod).with(priority).set(active: isActive)
  129.         case .equalOrGreater:   return topAnchor.constraint(greaterThanOrEqualTo:   anchor ?? view.topAnchor, constant: offset+offsetMod).with(priority).set(active: isActive)
  130.         }
  131.     }
  132.    
  133.     @discardableResult
  134.     public func bottomToTop(of view: Constrainable, offset: CGFloat = 0, relation: ConstraintRelation = .equal, priority: ConstraintPriority = .required, textOptions: TextLayoutOptions = [], isActive: Bool = true) -> Constraint {
  135.         return bottom(to: view, view.topAnchor, offset: offset, relation: relation, priority: priority, textOptions: textOptions, isActive: isActive)
  136.     }
  137.    
  138.     @discardableResult
  139.     public func bottom(to view: Constrainable, _ anchor: NSLayoutYAxisAnchor? = nil, offset: CGFloat = 0, relation: ConstraintRelation = .equal, priority: ConstraintPriority = .required, textOptions: TextLayoutOptions = [], isActive: Bool = true) -> Constraint {
  140.         let toEdge = (anchor != nil) ? ViewEdge.top : ViewEdge.bottom
  141.         let offsetMod = self.getOffsetModifier(to: view, edge: ViewEdge.top, toEdge: toEdge, textOptions: textOptions)
  142.        
  143.         switch relation {
  144.         case .equal:            return bottomAnchor.constraint(equalTo:                 anchor ?? view.bottomAnchor, constant: offset+offsetMod).with(priority).set(active: isActive)
  145.         case .equalOrLess:      return bottomAnchor.constraint(lessThanOrEqualTo:       anchor ?? view.bottomAnchor, constant: offset+offsetMod).with(priority).set(active: isActive)
  146.         case .equalOrGreater:   return bottomAnchor.constraint(greaterThanOrEqualTo:    anchor ?? view.bottomAnchor, constant: offset+offsetMod).with(priority).set(active: isActive)
  147.         }
  148.     }
  149.    
  150.     @discardableResult
  151.     public func centerY(to view: Constrainable, _ anchor: NSLayoutYAxisAnchor? = nil, offset: CGFloat = 0, priority: ConstraintPriority = .required, textOptions: TextLayoutOptions = [], isActive: Bool = true) -> Constraint {
  152.         // Calculate a special offset mod
  153.         let myTI = self.typographyInsets()
  154.         let toTI = view.typographyInsets()
  155.         let offsetMod = ((toTI.top+toTI.bottom)-(myTI.top+myTI.bottom))/2
  156.        
  157.         let constraint = centerYAnchor.constraint(equalTo: anchor ?? view.centerYAnchor, constant: offset+offsetMod).with(priority)
  158.         constraint.isActive = isActive
  159.         return constraint
  160.     }
  161. }
Add Comment
Please, Sign In to add comment