SnapKit 源码解读(五):Models
Models 里面的所有文件,都是用來對約束建模使用的。
Typealiases
Typealiases 為跨平臺能力定義了一套公用的類。
#if os(iOS) || os(tvOS)import UIKittypealias LayoutRelation = NSLayoutRelationtypealias LayoutAttribute = NSLayoutAttributetypealias LayoutPriority = UILayoutPriority #elseimport AppKittypealias LayoutRelation = NSLayoutConstraint.Relationtypealias LayoutAttribute = NSLayoutConstraint.Attributetypealias LayoutPriority = NSLayoutConstraint.Priority #endif 復制代碼Constraint
Constraint 類是對 NSLayoutConstraint 的建模,其中包含了構建應用所需要的所有必備參數,更新和激活約束的相關方法。
初始化方法
public final class Constraint {internal let sourceLocation: (String, UInt)internal let label: String?private let from: ConstraintItemprivate let to: ConstraintItemprivate let relation: ConstraintRelationprivate let multiplier: ConstraintMultiplierTargetprivate var constant: ConstraintConstantTarget {didSet {self.updateConstantAndPriorityIfNeeded()}}private var priority: ConstraintPriorityTarget {didSet {self.updateConstantAndPriorityIfNeeded()}}public var layoutConstraints: [LayoutConstraint]public var isActive: Bool {for layoutConstraint in self.layoutConstraints {if layoutConstraint.isActive {return true}}return false}// MARK: Initializationinternal init(from: ConstraintItem,to: ConstraintItem,relation: ConstraintRelation,sourceLocation: (String, UInt),label: String?,multiplier: ConstraintMultiplierTarget,constant: ConstraintConstantTarget,priority: ConstraintPriorityTarget) {self.from = fromself.to = toself.relation = relationself.sourceLocation = sourceLocationself.label = labelself.multiplier = multiplierself.constant = constantself.priority = priorityself.layoutConstraints = []// get attributeslet layoutFromAttributes = self.from.attributes.layoutAttributeslet layoutToAttributes = self.to.attributes.layoutAttributes// get layout fromlet layoutFrom = self.from.layoutConstraintItem!// get relationlet layoutRelation = self.relation.layoutRelationfor layoutFromAttribute in layoutFromAttributes {// get layout to attributelet layoutToAttribute: LayoutAttribute#if os(iOS) || os(tvOS)if layoutToAttributes.count > 0 {if self.from.attributes == .edges && self.to.attributes == .margins {switch layoutFromAttribute {case .left:layoutToAttribute = .leftMargincase .right:layoutToAttribute = .rightMargincase .top:layoutToAttribute = .topMargincase .bottom:layoutToAttribute = .bottomMargindefault:fatalError()}} else if self.from.attributes == .margins && self.to.attributes == .edges {switch layoutFromAttribute {case .leftMargin:layoutToAttribute = .leftcase .rightMargin:layoutToAttribute = .rightcase .topMargin:layoutToAttribute = .topcase .bottomMargin:layoutToAttribute = .bottomdefault:fatalError()}} else if self.from.attributes == self.to.attributes {layoutToAttribute = layoutFromAttribute} else {layoutToAttribute = layoutToAttributes[0]}} else {if self.to.target == nil && (layoutFromAttribute == .centerX || layoutFromAttribute == .centerY) {layoutToAttribute = layoutFromAttribute == .centerX ? .left : .top} else {layoutToAttribute = layoutFromAttribute}}#elseif self.from.attributes == self.to.attributes {layoutToAttribute = layoutFromAttribute} else if layoutToAttributes.count > 0 {layoutToAttribute = layoutToAttributes[0]} else {layoutToAttribute = layoutFromAttribute}#endif// get layout constantlet layoutConstant: CGFloat = self.constant.constraintConstantTargetValueFor(layoutAttribute: layoutToAttribute)// get layout tovar layoutTo: AnyObject? = self.to.target// use superview if possibleif layoutTo == nil && layoutToAttribute != .width && layoutToAttribute != .height {layoutTo = layoutFrom.superview}// create layout constraintlet layoutConstraint = LayoutConstraint(item: layoutFrom,attribute: layoutFromAttribute,relatedBy: layoutRelation,toItem: layoutTo,attribute: layoutToAttribute,multiplier: self.multiplier.constraintMultiplierTargetValue,constant: layoutConstant)// set labellayoutConstraint.label = self.label// set prioritylayoutConstraint.priority = LayoutPriority(rawValue: self.priority.constraintPriorityTargetValue)// set constraintlayoutConstraint.constraint = self// appendself.layoutConstraints.append(layoutConstraint)}}...} 復制代碼初始化方法接受所有創建一個 NSLayoutConstraint 所需要的參數,最終生成一個 layoutConstraint 并放入自身的數組里。
final
如果你想要一個類不能被其他的類繼承,請加上 final 標記
公共方法
Constraint 類提供了一些 public 的方法,用來供我們激活/取消激活約束,修改 constant。
public final class Constraint {...public func activate() {self.activateIfNeeded()}public func deactivate() {self.deactivateIfNeeded()}@discardableResultpublic func update(offset: ConstraintOffsetTarget) -> Constraint {self.constant = offset.constraintOffsetTargetValuereturn self}@discardableResultpublic func update(inset: ConstraintInsetTarget) -> Constraint {self.constant = inset.constraintInsetTargetValuereturn self}@discardableResultpublic func update(priority: ConstraintPriorityTarget) -> Constraint {self.priority = priority.constraintPriorityTargetValuereturn self}...} 復制代碼內部方法
Constraint 提供了三個內部的方法,分別用來更新優先級,激活約束,取消激活約束。
public final class Constraint {...// MARK: Internalinternal func updateConstantAndPriorityIfNeeded() {for layoutConstraint in self.layoutConstraints {let attribute = (layoutConstraint.secondAttribute == .notAnAttribute) ? layoutConstraint.firstAttribute : layoutConstraint.secondAttributelayoutConstraint.constant = self.constant.constraintConstantTargetValueFor(layoutAttribute: attribute)let requiredPriority = ConstraintPriority.required.valueif (layoutConstraint.priority.rawValue < requiredPriority), (self.priority.constraintPriorityTargetValue != requiredPriority) {layoutConstraint.priority = LayoutPriority(rawValue: self.priority.constraintPriorityTargetValue)}}}internal func activateIfNeeded(updatingExisting: Bool = false) {guard let item = self.from.layoutConstraintItem else {print("WARNING: SnapKit failed to get from item from constraint. Activate will be a no-op.")return}let layoutConstraints = self.layoutConstraintsif updatingExisting {var existingLayoutConstraints: [LayoutConstraint] = []for constraint in item.constraints {existingLayoutConstraints += constraint.layoutConstraints}for layoutConstraint in layoutConstraints {let existingLayoutConstraint = existingLayoutConstraints.first { $0 == layoutConstraint }guard let updateLayoutConstraint = existingLayoutConstraint else {fatalError("Updated constraint could not find existing matching constraint to update: \(layoutConstraint)")}let updateLayoutAttribute = (updateLayoutConstraint.secondAttribute == .notAnAttribute) ? updateLayoutConstraint.firstAttribute : updateLayoutConstraint.secondAttributeupdateLayoutConstraint.constant = self.constant.constraintConstantTargetValueFor(layoutAttribute: updateLayoutAttribute)}} else {NSLayoutConstraint.activate(layoutConstraints)item.add(constraints: [self])}}internal func deactivateIfNeeded() {guard let item = self.from.layoutConstraintItem else {print("WARNING: SnapKit failed to get from item from constraint. Deactivate will be a no-op.")return}let layoutConstraints = self.layoutConstraintsNSLayoutConstraint.deactivate(layoutConstraints)item.remove(constraints: [self])} } 復制代碼值得一看的是 activateIfNeeded 方法,其實就是找出現有的約束,并更新對應的 constant。
ConstraintDescription
ConstraintDescription 是內部用來建模 Constraint 類型的模型,其中有一個懶加載屬性 constraint,第一次使用時才會創建真正的 Constraint 對象,避免了額外的性能消耗。
public class ConstraintDescription {internal let item: LayoutConstraintIteminternal var attributes: ConstraintAttributesinternal var relation: ConstraintRelation? = nilinternal var sourceLocation: (String, UInt)? = nilinternal var label: String? = nilinternal var related: ConstraintItem? = nilinternal var multiplier: ConstraintMultiplierTarget = 1.0internal var constant: ConstraintConstantTarget = 0.0internal var priority: ConstraintPriorityTarget = 1000.0internal lazy var constraint: Constraint? = {guard let relation = self.relation,let related = self.related,let sourceLocation = self.sourceLocation else {return nil}let from = ConstraintItem(target: self.item, attributes: self.attributes)return Constraint(from: from,to: related,relation: relation,sourceLocation: sourceLocation,label: self.label,multiplier: self.multiplier,constant: self.constant,priority: self.priority)}()// MARK: Initializationinternal init(item: LayoutConstraintItem, attributes: ConstraintAttributes) {self.item = itemself.attributes = attributes}} 復制代碼ConstraintInsets、ConstraintConfig、ConstraintView、ConstraintLayoutGuide 和 ConstraintLayoutSupport
這五個 swift 文件中都是對現有類型的 typealias,沒有什么特別的。
ConstraintRelation
ConstraintRelation 是一個枚舉類型,用來在框架內部表示等于、大于和小于的關系。
internal enum ConstraintRelation : Int {case equal = 1case lessThanOrEqualcase greaterThanOrEqualinternal var layoutRelation: LayoutRelation {get {switch(self) {case .equal:return .equalcase .lessThanOrEqual:return .lessThanOrEqualcase .greaterThanOrEqual:return .greaterThanOrEqual}}} } 復制代碼ConstraintAttributes
ConstraintAttributes 也是枚舉,不過與尋常用到的枚舉不同,它是可以組合而非非此即彼的枚舉,即位掩碼。
OptionSet
Swift 中給出的位掩碼解決方案,是通過 struct 遵守 OptionSet 協議來實現的,為了與枚舉類似,ConstraintAttributes 也定義了一個內部只讀屬性來作為 rawValue。
internal struct ConstraintAttributes : OptionSet {internal init(rawValue: UInt) {self.rawValue = rawValue}internal init(_ rawValue: UInt) {self.init(rawValue: rawValue)}internal init(nilLiteral: ()) {self.rawValue = 0}internal private(set) var rawValue: UInt... }復制代碼同時,為了方便使用,還定義了一批計算屬性供我們快速創建和使用對應的枚舉:
internal struct ConstraintAttributes : OptionSet {...// normalinternal static var none: ConstraintAttributes { return self.init(0) }internal static var left: ConstraintAttributes { return self.init(1) }internal static var top: ConstraintAttributes { return self.init(2) }internal static var right: ConstraintAttributes { return self.init(4) }internal static var bottom: ConstraintAttributes { return self.init(8) }internal static var leading: ConstraintAttributes { return self.init(16) }internal static var trailing: ConstraintAttributes { return self.init(32) }internal static var width: ConstraintAttributes { return self.init(64) }internal static var height: ConstraintAttributes { return self.init(128) }internal static var centerX: ConstraintAttributes { return self.init(256) }internal static var centerY: ConstraintAttributes { return self.init(512) }internal static var lastBaseline: ConstraintAttributes { return self.init(1024) }@available(iOS 8.0, OSX 10.11, *)internal static var firstBaseline: ConstraintAttributes { return self.init(2048) }@available(iOS 8.0, *)internal static var leftMargin: ConstraintAttributes { return self.init(4096) }@available(iOS 8.0, *)internal static var rightMargin: ConstraintAttributes { return self.init(8192) }@available(iOS 8.0, *)internal static var topMargin: ConstraintAttributes { return self.init(16384) }@available(iOS 8.0, *)internal static var bottomMargin: ConstraintAttributes { return self.init(32768) }@available(iOS 8.0, *)internal static var leadingMargin: ConstraintAttributes { return self.init(65536) }@available(iOS 8.0, *)internal static var trailingMargin: ConstraintAttributes { return self.init(131072) }@available(iOS 8.0, *)internal static var centerXWithinMargins: ConstraintAttributes { return self.init(262144) }@available(iOS 8.0, *)internal static var centerYWithinMargins: ConstraintAttributes { return self.init(524288) }// aggregatesinternal static var edges: ConstraintAttributes { return self.init(15) }internal static var size: ConstraintAttributes { return self.init(192) }internal static var center: ConstraintAttributes { return self.init(768) }@available(iOS 8.0, *)internal static var margins: ConstraintAttributes { return self.init(61440) }@available(iOS 8.0, *)internal static var centerWithinMargins: ConstraintAttributes { return self.init(786432) }...} 復制代碼每一個計算屬性初始化對應枚舉的時候,rawValue 都是 2 的 n 次方,也是為了避免運算時出現沖突。
自定義運算符
Swift 可以自定義運算符,來簡化復雜的方法調用。
internal func + (left: ConstraintAttributes, right: ConstraintAttributes) -> ConstraintAttributes {return left.union(right) }internal func +=(left: inout ConstraintAttributes, right: ConstraintAttributes) {left.formUnion(right) }internal func -=(left: inout ConstraintAttributes, right: ConstraintAttributes) {left.subtract(right) }internal func ==(left: ConstraintAttributes, right: ConstraintAttributes) -> Bool {return left.rawValue == right.rawValue } 復制代碼ConstraintItem
ConstraintItem 建模了 target 和 attributes 之間的關系,同樣定義了自定義運算符,用來判斷相等性。
public final class ConstraintItem {internal weak var target: AnyObject?internal let attributes: ConstraintAttributesinternal init(target: AnyObject?, attributes: ConstraintAttributes) {self.target = targetself.attributes = attributes}internal var layoutConstraintItem: LayoutConstraintItem? {return self.target as? LayoutConstraintItem}}public func ==(lhs: ConstraintItem, rhs: ConstraintItem) -> Bool {// pointer equalityguard lhs !== rhs else {return true}// must both have valid targets and identical attributesguard let target1 = lhs.target,let target2 = rhs.target,target1 === target2 && lhs.attributes == rhs.attributes else {return false}return true } 復制代碼LayoutConstraint
LayoutConstraint 是 NSLayoutConstraint 的子類,做了兩件事情:1、定義了新的計算屬性 label,其實是對 identifier 的別稱。2、自定義了預算符,用來判斷約束之間的相等性。
public class LayoutConstraint : NSLayoutConstraint {public var label: String? {get {return self.identifier}set {self.identifier = newValue}}internal weak var constraint: Constraint? = nil}internal func ==(lhs: LayoutConstraint, rhs: LayoutConstraint) -> Bool {guard lhs.firstItem === rhs.firstItem &&lhs.secondItem === rhs.secondItem &&lhs.firstAttribute == rhs.firstAttribute &&lhs.secondAttribute == rhs.secondAttribute &&lhs.relation == rhs.relation &&lhs.priority == rhs.priority &&lhs.multiplier == rhs.multiplier else {return false}return true } 復制代碼LayoutConstraintItem
LayoutConstraintItem 是一個僅供 class 使用的協議,繼承這個協議的有 ConstraintLayoutGuide 和 ConstraintView,目的在于為這兩者通過協議擴展提供一些方法和關聯對象。
public protocol LayoutConstraintItem: class { }@available(iOS 9.0, OSX 10.11, *) extension ConstraintLayoutGuide : LayoutConstraintItem { }extension ConstraintView : LayoutConstraintItem { }extension LayoutConstraintItem {internal func prepare() {if let view = self as? ConstraintView {view.translatesAutoresizingMaskIntoConstraints = false}}internal var superview: ConstraintView? {if let view = self as? ConstraintView {return view.superview}if #available(iOS 9.0, OSX 10.11, *), let guide = self as? ConstraintLayoutGuide {return guide.owningView}return nil}internal var constraints: [Constraint] {return self.constraintsSet.allObjects as! [Constraint]}internal func add(constraints: [Constraint]) {let constraintsSet = self.constraintsSetfor constraint in constraints {constraintsSet.add(constraint)}}internal func remove(constraints: [Constraint]) {let constraintsSet = self.constraintsSetfor constraint in constraints {constraintsSet.remove(constraint)}}private var constraintsSet: NSMutableSet {let constraintsSet: NSMutableSetif let existing = objc_getAssociatedObject(self, &constraintsKey) as? NSMutableSet {constraintsSet = existing} else {constraintsSet = NSMutableSet()objc_setAssociatedObject(self, &constraintsKey, constraintsSet, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)}return constraintsSet}} private var constraintsKey: UInt8 = 0 復制代碼其中 constraintsSet 是管理施加在 ConstraintLayoutGuide 和 ConstraintView 上的約束,便于更新約束。
ConstraintPriority
ConstraintPriority 是對約束優先級進行定義的一個結構體并提供了判斷相等性和差距的方便方法,唯一需要在意的是在 OS X 平臺上 medium 的定義是 501,不知道為啥~
public struct ConstraintPriority : ExpressibleByFloatLiteral, Equatable, Strideable {public typealias FloatLiteralType = Floatpublic let value: Floatpublic init(floatLiteral value: Float) {self.value = value}public init(_ value: Float) {self.value = value}public static var required: ConstraintPriority {return 1000.0}public static var high: ConstraintPriority {return 750.0}public static var medium: ConstraintPriority {#if os(OSX)return 501.0#elsereturn 500.0#endif}public static var low: ConstraintPriority {return 250.0}public static func ==(lhs: ConstraintPriority, rhs: ConstraintPriority) -> Bool {return lhs.value == rhs.value}// MARK: Strideablepublic func advanced(by n: FloatLiteralType) -> ConstraintPriority {return ConstraintPriority(floatLiteral: value + n)}public func distance(to other: ConstraintPriority) -> FloatLiteralType {return other.value - value} } 復制代碼原文地址:SnapKit 源碼解讀(五):Models
如果覺得我寫的還不錯,請關注我的微博@小橘爺,最新文章即時推送~
總結
以上是生活随笔為你收集整理的SnapKit 源码解读(五):Models的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 牛客网 小白赛4 A三角形【贪心】
- 下一篇: Win10打开IE自动跳转至Edge解决