SnapKit 源码解读(三):Maker

MakerSnapKit 中最核心的概念,所有关于约束的操作都是通过 Maker 来进行管理和操作的。

ConstraintMaker

ConstraintMaker 是施加、更新和重置约束三个功能的核心类,与之对应的也实现了相关的类方法。同时其还声明了很多计算属性,用来配置约束使用。

初始化方法

ConstraintMaker 是通过 LayoutConstraintItem 类型的 item 参数进行初始化的,LayoutConstraintItem 是一个模型类,保存了所有的约束。

private let item: LayoutConstraintItem
    
internal init(item: LayoutConstraintItem) {
    self.item = item
    self.item.prepare()
}
复制代码

计算属性

ConstraintMaker 内部有很多的计算属性,用以辅助描述约束。

public var left: ConstraintMakerExtendable {
    return self.makeExtendableWithAttributes(.left)
}
    
public var top: ConstraintMakerExtendable {
    return self.makeExtendableWithAttributes(.top)
}
    
public var bottom: ConstraintMakerExtendable {
    return self.makeExtendableWithAttributes(.bottom)
}
    
public var right: ConstraintMakerExtendable {
    return self.makeExtendableWithAttributes(.right)
}
    
public var leading: ConstraintMakerExtendable {
    return self.makeExtendableWithAttributes(.leading)
}
    
public var trailing: ConstraintMakerExtendable {
    return self.makeExtendableWithAttributes(.trailing)
}
    
public var width: ConstraintMakerExtendable {
    return self.makeExtendableWithAttributes(.width)
}
    
public var height: ConstraintMakerExtendable {
    return self.makeExtendableWithAttributes(.height)
}
  
public var centerX: ConstraintMakerExtendable {
    return self.makeExtendableWithAttributes(.centerX)
}
   
public var centerY: ConstraintMakerExtendable {
    return self.makeExtendableWithAttributes(.centerY)
}
  
@available(*, deprecated:3.0, message:"Use lastBaseline instead")
public var baseline: ConstraintMakerExtendable {
    return self.makeExtendableWithAttributes(.lastBaseline)
}
    
public var lastBaseline: ConstraintMakerExtendable {
    return self.makeExtendableWithAttributes(.lastBaseline)
}
    
@available(iOS 8.0, OSX 10.11, *)
public var firstBaseline: ConstraintMakerExtendable {
    return self.makeExtendableWithAttributes(.firstBaseline)
}
    
@available(iOS 8.0, *)
public var leftMargin: ConstraintMakerExtendable {
    return self.makeExtendableWithAttributes(.leftMargin)
}
    
@available(iOS 8.0, *)
public var rightMargin: ConstraintMakerExtendable {
    return self.makeExtendableWithAttributes(.rightMargin)
}
    
@available(iOS 8.0, *)
public var topMargin: ConstraintMakerExtendable {
    return self.makeExtendableWithAttributes(.topMargin)
}
    
@available(iOS 8.0, *)
public var bottomMargin: ConstraintMakerExtendable {
    return self.makeExtendableWithAttributes(.bottomMargin)
}
    
@available(iOS 8.0, *)
public var leadingMargin: ConstraintMakerExtendable {
    return self.makeExtendableWithAttributes(.leadingMargin)
}
    
@available(iOS 8.0, *)
public var trailingMargin: ConstraintMakerExtendable {
    return self.makeExtendableWithAttributes(.trailingMargin)
}
    
@available(iOS 8.0, *)
public var centerXWithinMargins: ConstraintMakerExtendable {
    return self.makeExtendableWithAttributes(.centerXWithinMargins)
}
    
@available(iOS 8.0, *)
public var centerYWithinMargins: ConstraintMakerExtendable {
    return self.makeExtendableWithAttributes(.centerYWithinMargins)
}
    
public var edges: ConstraintMakerExtendable {
    return self.makeExtendableWithAttributes(.edges)
}
public var size: ConstraintMakerExtendable {
    return self.makeExtendableWithAttributes(.size)
}
public var center: ConstraintMakerExtendable {
    return self.makeExtendableWithAttributes(.center)
}
    
@available(iOS 8.0, *)
public var margins: ConstraintMakerExtendable {
    return self.makeExtendableWithAttributes(.margins)
}
    
@available(iOS 8.0, *)
public var centerWithinMargins: ConstraintMakerExtendable {
    return self.makeExtendableWithAttributes(.centerWithinMargins)
}
复制代码

所有的这些计算属性都会通过内部的一个 makeExtendableWithAttributes 方法初始化一个 ConstraintMakerExtendable 类型的对象,并返回之,makeExtendableWithAttributes 方法如下所示:

private var descriptions = [ConstraintDescription]()

internal func makeExtendableWithAttributes(_ attributes: ConstraintAttributes) -> ConstraintMakerExtendable {
    let description = ConstraintDescription(item: self.item, attributes: attributes)
    self.descriptions.append(description)
    return ConstraintMakerExtendable(description)
}
复制代码

该方法会根据 self.item 初始化一个 ConstraintDescription 对象,并将该对象存储在 descriptions 数组中,再创造一个 ConstraintMakerExtendable 类型的对象并返回之。

prepareConstraints

prepareConstraints 方法会先生成一个 ConstraintMaker 类型的对象 maker,然后将 maker 通过 closure 传递给外界做一些属性的配置,接着遍历 maker.descriptions 取出其 constraint 属性,并返回之。

internal static func prepareConstraints(item: LayoutConstraintItem, closure: (_ make: ConstraintMaker) -> Void) -> [Constraint] {
    let maker = ConstraintMaker(item: item)
    closure(maker)
    var constraints: [Constraint] = []
    for description in maker.descriptions {
        guard let constraint = description.constraint else {
            continue
        }
        constraints.append(constraint)
    }
    return constraints
}
复制代码

makeConstraints

makeConstraints 方法与 prepareConstraints 方法大同小异,区别在于,makeConstraints 方法的最后不是 return constraints,而是挨个 constraint.activateIfNeeded(updatingExisting: false)

internal static func makeConstraints(item: LayoutConstraintItem, closure: (_ make: ConstraintMaker) -> Void) {
    let maker = ConstraintMaker(item: item)
    closure(maker)
    var constraints: [Constraint] = []
    for description in maker.descriptions {
        guard let constraint = description.constraint else {
            continue
        }
        constraints.append(constraint)
    }
    for constraint in constraints {
        constraint.activateIfNeeded(updatingExisting: false)
    }
}
复制代码

remakeConstraints

移除约束的操作分为两步,第一步:移除现有约束,第二步:重新添加约束。

internal static func remakeConstraints(item: LayoutConstraintItem, closure: (_ make: ConstraintMaker) -> Void) {
    self.removeConstraints(item: item)
    self.makeConstraints(item: item, closure: closure)
}
复制代码

移除约束操作就是从 itemconstraints 属性里所有 constraint 挨个失效。

internal static func removeConstraints(item: LayoutConstraintItem) {
    let constraints = item.constraints
    for constraint in constraints {
        constraint.deactivateIfNeeded()
    }
}
复制代码

updateConstraints

更新约束的操作也分为两部分:首先会判断当前是否已经施加过约束,如果没有,则直接转入 makeConstraints 流程。其余操作与 makeConstraints 类似,区别就是 updatingExisting 参数为 true

internal static func updateConstraints(item: LayoutConstraintItem, closure: (_ make: ConstraintMaker) -> Void) {
    guard item.constraints.count > 0 else {
        self.makeConstraints(item: item, closure: closure)
        return
    }
        
    let maker = ConstraintMaker(item: item)
    closure(maker)
    var constraints: [Constraint] = []
    for description in maker.descriptions {
        guard let constraint = description.constraint else {
            continue
        }
        constraints.append(constraint)
    }
    for constraint in constraints {
        constraint.activateIfNeeded(updatingExisting: true)
    }
}
复制代码

ConstraintMakerFinalizable

ConstraintMakerFinalizable 是对 ConstraintDescription 的一层包装,并提供了一些方便方法供我们设置和获取一些值。

public class ConstraintMakerFinalizable {
    
    internal let description: ConstraintDescription
    
    internal init(_ description: ConstraintDescription) {
        self.description = description
    }
    
    @discardableResult
    public func labeled(_ label: String) -> ConstraintMakerFinalizable {
        self.description.label = label
        return self
    }
    
    public var constraint: Constraint {
        return self.description.constraint!
    }
    
}
复制代码

其中,labeled 方法的最后 return self,是为了可以实现链式调用。

ConstraintMakerPriortizable

ConstraintMakerPriortizableConstraintMakerFinalizable 的子类,扩充了关于优先级的一些方法。

public class ConstraintMakerPriortizable: ConstraintMakerFinalizable {
    
    @discardableResult
    public func priority(_ amount: ConstraintPriority) -> ConstraintMakerFinalizable {
        self.description.priority = amount.value
        return self
    }
    
    @discardableResult
    public func priority(_ amount: ConstraintPriorityTarget) -> ConstraintMakerFinalizable {
        self.description.priority = amount
        return self
    }

}
复制代码

ConstraintMakerEditable

ConstraintMakerEditableConstraintMakerPriortizable 的子类,扩充了关于设置 multiplierconstant 的方法。

public class ConstraintMakerEditable: ConstraintMakerPriortizable {

    @discardableResult
    public func multipliedBy(_ amount: ConstraintMultiplierTarget) -> ConstraintMakerEditable {
        self.description.multiplier = amount
        return self
    }
    
    @discardableResult
    public func dividedBy(_ amount: ConstraintMultiplierTarget) -> ConstraintMakerEditable {
        return self.multipliedBy(1.0 / amount.constraintMultiplierTargetValue)
    }
    
    @discardableResult
    public func offset(_ amount: ConstraintOffsetTarget) -> ConstraintMakerEditable {
        self.description.constant = amount.constraintOffsetTargetValue
        return self
    }
    
    @discardableResult
    public func inset(_ amount: ConstraintInsetTarget) -> ConstraintMakerEditable {
        self.description.constant = amount.constraintInsetTargetValue
        return self
    }
    
}
复制代码

ConstraintMakerRelatable

ConstraintMakerRelatable 是对 ConstraintDescription 的一层包装,作用是为 ConstraintDescription 设置 relation 提供方便方法。有 equalToequalToSuperviewlessThanOrEqualTolessThanOrEqualToSuperviewgreaterThanOrEqualTogreaterThanOrEqualToSuperview 等一系列方法:

@discardableResult
public func equalTo(_ other: ConstraintRelatableTarget, _ file: String = #file, _ line: UInt = #line) -> ConstraintMakerEditable {
    return self.relatedTo(other, relation: .equal, file: file, line: line)
}
    
@discardableResult
public func equalToSuperview(_ file: String = #file, _ line: UInt = #line) -> ConstraintMakerEditable {
    guard let other = self.description.item.superview else {
        fatalError("Expected superview but found nil when attempting make constraint `equalToSuperview`.")
    }
    return self.relatedTo(other, relation: .equal, file: file, line: line)
}
    
@discardableResult
public func lessThanOrEqualTo(_ other: ConstraintRelatableTarget, _ file: String = #file, _ line: UInt = #line) -> ConstraintMakerEditable {
    return self.relatedTo(other, relation: .lessThanOrEqual, file: file, line: line)
}
    
@discardableResult
public func lessThanOrEqualToSuperview(_ file: String = #file, _ line: UInt = #line) -> ConstraintMakerEditable {
    guard let other = self.description.item.superview else {
        fatalError("Expected superview but found nil when attempting make constraint `lessThanOrEqualToSuperview`.")
    }
    return self.relatedTo(other, relation: .lessThanOrEqual, file: file, line: line)
}
    
@discardableResult
public func greaterThanOrEqualTo(_ other: ConstraintRelatableTarget, _ file: String = #file, line: UInt = #line) -> ConstraintMakerEditable {
    return self.relatedTo(other, relation: .greaterThanOrEqual, file: file, line: line)
}
    
@discardableResult
public func greaterThanOrEqualToSuperview(_ file: String = #file, line: UInt = #line) -> ConstraintMakerEditable {
    guard let other = self.description.item.superview else {
        fatalError("Expected superview but found nil when attempting make constraint `greaterThanOrEqualToSuperview`.")
    }
    return self.relatedTo(other, relation: .greaterThanOrEqual, file: file, line: line)
}
复制代码

其中有两个点值得学习。

#file 和 #line

Swift 为我们提供了设置方法参数默认值的方法,就是在方法声明处直接为参数赋值:

..._ file: String = #file, _ line: UInt = #line...
复制代码

#file#line 则是系统为我们提供的编译符号,供我们获取文件名和行号,更多细节可以参考这篇博客:LOG 输出

fatalError()

有的时候,该崩还是得崩,fatalError() 助你一崩到底,还能添加附加信息,哈啤。

除了以上两点,所有这些方法均调用了内部的 relatedTo 方法:

internal func relatedTo(_ other: ConstraintRelatableTarget, relation: ConstraintRelation, file: String, line: UInt) -> ConstraintMakerEditable {
    let related: ConstraintItem
    let constant: ConstraintConstantTarget
        
    if let other = other as? ConstraintItem {
        guard other.attributes == ConstraintAttributes.none ||
                other.attributes.layoutAttributes.count <= 1 ||
                other.attributes.layoutAttributes == self.description.attributes.layoutAttributes ||
                other.attributes == .edges && self.description.attributes == .margins ||
                other.attributes == .margins && self.description.attributes == .edges else {
            fatalError("Cannot constraint to multiple non identical attributes. (\(file), \(line))");
        }
            
        related = other
        constant = 0.0
    } else if let other = other as? ConstraintView {
        related = ConstraintItem(target: other, attributes: ConstraintAttributes.none)
        constant = 0.0
    } else if let other = other as? ConstraintConstantTarget {
        related = ConstraintItem(target: nil, attributes: ConstraintAttributes.none)
        constant = other
    } else if #available(iOS 9.0, OSX 10.11, *), let other = other as? ConstraintLayoutGuide {
        related = ConstraintItem(target: other, attributes: ConstraintAttributes.none)
        constant = 0.0
    } else {
        fatalError("Invalid constraint. (\(file), \(line))")
    }
        
    let editable = ConstraintMakerEditable(self.description)
    editable.description.sourceLocation = (file, line)
    editable.description.relation = relation
    editable.description.related = related
    editable.description.constant = constant
    return editable
}
复制代码

该方法会对约束信息的合法性进行一些判断,进而生成一个 ConstraintMakerEditable 类型的对象并返回之。

ConstraintMakerExtendable

ConstraintMakerExtendableConstraintMakerRelatable 的父类,封装了一系列的计算属性,并有 side-effect 每次调用时对 description.attributes 的会有一些额外效果:

public class ConstraintMakerExtendable: ConstraintMakerRelatable {
    
    public var left: ConstraintMakerExtendable {
        self.description.attributes += .left
        return self
    }
    
    public var top: ConstraintMakerExtendable {
        self.description.attributes += .top
        return self
    }
    
    public var bottom: ConstraintMakerExtendable {
        self.description.attributes += .bottom
        return self
    }
    
    public var right: ConstraintMakerExtendable {
        self.description.attributes += .right
        return self
    }
    
    public var leading: ConstraintMakerExtendable {
        self.description.attributes += .leading
        return self
    }
    
    public var trailing: ConstraintMakerExtendable {
        self.description.attributes += .trailing
        return self
    }
    
    public var width: ConstraintMakerExtendable {
        self.description.attributes += .width
        return self
    }
    
    public var height: ConstraintMakerExtendable {
        self.description.attributes += .height
        return self
    }
    
    public var centerX: ConstraintMakerExtendable {
        self.description.attributes += .centerX
        return self
    }
    
    public var centerY: ConstraintMakerExtendable {
        self.description.attributes += .centerY
        return self
    }
    
    @available(*, deprecated:3.0, message:"Use lastBaseline instead")
    public var baseline: ConstraintMakerExtendable {
        self.description.attributes += .lastBaseline
        return self
    }
    
    public var lastBaseline: ConstraintMakerExtendable {
        self.description.attributes += .lastBaseline
        return self
    }
    
    @available(iOS 8.0, OSX 10.11, *)
    public var firstBaseline: ConstraintMakerExtendable {
        self.description.attributes += .firstBaseline
        return self
    }
    
    @available(iOS 8.0, *)
    public var leftMargin: ConstraintMakerExtendable {
        self.description.attributes += .leftMargin
        return self
    }
    
    @available(iOS 8.0, *)
    public var rightMargin: ConstraintMakerExtendable {
        self.description.attributes += .rightMargin
        return self
    }
    
    @available(iOS 8.0, *)
    public var topMargin: ConstraintMakerExtendable {
        self.description.attributes += .topMargin
        return self
    }
    
    @available(iOS 8.0, *)
    public var bottomMargin: ConstraintMakerExtendable {
        self.description.attributes += .bottomMargin
        return self
    }
    
    @available(iOS 8.0, *)
    public var leadingMargin: ConstraintMakerExtendable {
        self.description.attributes += .leadingMargin
        return self
    }
    
    @available(iOS 8.0, *)
    public var trailingMargin: ConstraintMakerExtendable {
        self.description.attributes += .trailingMargin
        return self
    }
    
    @available(iOS 8.0, *)
    public var centerXWithinMargins: ConstraintMakerExtendable {
        self.description.attributes += .centerXWithinMargins
        return self
    }
    
    @available(iOS 8.0, *)
    public var centerYWithinMargins: ConstraintMakerExtendable {
        self.description.attributes += .centerYWithinMargins
        return self
    }
    
    public var edges: ConstraintMakerExtendable {
        self.description.attributes += .edges
        return self
    }
    public var size: ConstraintMakerExtendable {
        self.description.attributes += .size
        return self
    }
    
    @available(iOS 8.0, *)
    public var margins: ConstraintMakerExtendable {
        self.description.attributes += .margins
        return self
    }
    
    @available(iOS 8.0, *)
    public var centerWithinMargins: ConstraintMakerExtendable {
        self.description.attributes += .centerWithinMargins
        return self
    }
    
}
复制代码

如果觉得我写的还不错,请关注我的微博@小橘爷,最新文章即时推送~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值