热点新闻
SnapKit源码分析
2023-07-20 10:28  浏览:1706  搜索引擎搜索“微商筹货网”
温馨提示:信息一旦丢失不一定找得到,请务必收藏信息以备急用!本站所有信息均是注册会员发布如遇到侵权请联系文章中的联系方式或客服删除!
联系我时,请说明是在微商筹货网看到的信息,谢谢。
展会发布 展会网站大全 报名观展合作 软文发布

SnapKit源码分析

Snapkit版本:5.6.0

1. 给谁做约束

ConstraintView:对iOS而言是UIView,对macOS而言是NSView

#if os(iOS) || os(tvOS) public typealias ConstraintView = UIView #else public typealias ConstraintView = NSView #endif

给ConstraintView扩展了snp属性,snp为ConstraintViewDSL结构体

public extension ConstraintView { var snp: ConstraintViewDSL { return ConstraintViewDSL(view: self) } }

ConstraintViewDSL

在ConstraintViewDSL中提供了prepareConstraints、makeConstraints等我们经常调用的方法。

public struct ConstraintViewDSL: ConstraintAttributesDSL { @discardableResult public func prepareConstraints(_ closure: (_ make: ConstraintMaker) -> Void) -> [Constraint] { return ConstraintMaker.prepareConstraints(item: self.view, closure: closure) } public func makeConstraints(_ closure: (_ make: ConstraintMaker) -> Void) { ConstraintMaker.makeConstraints(item: self.view, closure: closure) } public func remakeConstraints(_ closure: (_ make: ConstraintMaker) -> Void) { ConstraintMaker.remakeConstraints(item: self.view, closure: closure) } public func updateConstraints(_ closure: (_ make: ConstraintMaker) -> Void) { ConstraintMaker.updateConstraints(item: self.view, closure: closure) } //.... internal init(view: ConstraintView) { self.view = view } }

(1)ConstraintViewDSL遵循ConstraintAttributesDSL协议,ConstraintAttributesDSL主要是增加了iOS 8.0和OSX 10.11之后的新的属性;

(2)ConstraintAttributesDSL遵循ConstraintBasicAttributesDSL协议,ConstraintBasicAttributesDSL主要是一些如left、top、right、size等基础的布局属性。

(3)通过internal init(view: ConstraintView)方法将要设置约束的view赋值给self.view

2. 分析设置约束的过程

通过分析ConstraintViewDSL的makeConstraints方法,了解设置约束的过程

public func makeConstraints(_ closure: (_ make: ConstraintMaker) -> Void) { ConstraintMaker.makeConstraints(item: self.view, closure: closure) }

这里通过调用ConstraintMaker的makeConstraints来实现,通过prepareConstraints构造Constraint后,进行逐个添加和激活

internal static func makeConstraints(item: LayoutConstraintItem, closure: (_ make: ConstraintMaker) -> Void) { let constraints = prepareConstraints(item: item, closure: closure) for constraint in constraints { constraint.activateIfNeeded(updatingExisting: false) } } 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 }

扩充属性

(1)ConstraintMaker:就是我们常写的makeConstraints回调中make的类型。

LayoutConstraintItem:是遵循AnyObject的一个协议,扩展了prepare、superview、constraints、add、remove、constraintsSet属性和方法 因为ConstraintView扩展了这个协议,所以可以直接传ConstraintView类型

(2)ConstraintMaker 包含left、top、centerX等基本属性,且返回ConstraintMakerExtendable,使得其能链式调用

public class 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) } //... }

通过ConstraintMaker的makeExtendableWithAttributes方法,不断新增描述中的属性(description.attributes)

其中attributes遵循OptionSet, ExpressibleByIntegerLiteral协议。

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

扩充值

(3)ConstraintMakerExtendable遵循ConstraintMakerRelatable协议,扩充了equalTo、equalToSuperview、lessThanOrEqualTo、greaterThanOrEqualTo等方法。
这些方法最终都会调用ConstraintMakerRelatable的relatedTo方法,将约束描述补充,并返回ConstraintMakerEditable类型。

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 || other.attributes == .directionalEdges && self.description.attributes == .directionalMargins || other.attributes == .directionalMargins && self.description.attributes == .directionalEdges 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类型包含multipliedBy、offset、dividedBy、inset等方法,支持对值做相应计算。

public class ConstraintMakerEditable: ConstraintMakerPrioritizable { @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 } #if os(iOS) || os(tvOS) @discardableResult @available(iOS 11.0, tvOS 11.0, *) public func inset(_ amount: ConstraintDirectionalInsetTarget) -> ConstraintMakerEditable { self.description.constant = amount.constraintDirectionalInsetTargetValue return self } #endif }

ConstraintMakerEditable继承自ConstraintMakerPrioritizable

扩充优先级

ConstraintMakerPrioritizable包含了优先级相关的方法priority、priorityRequired、priorityHigh、priorityMedium、priorityLow

ConstraintMakerPrioritizable继承自ConstraintMakerFinalizable

完整描述

ConstraintMakerFinalizable
一个只有一个类型为 ConstraintDescription 的属性的类,正如它的类名,有一个 ConstraintMakerFinalizable 实例,就得到了对于一个约束的完整描述。

过程

blackView.snp.makeConstraints { make in make.center.equalTo(view) make.size.equalTo(CGSize(width: 100, height: 100)) }

(1)回到ConstraintMaker的prepareConstraints方法,根据需要对属性、值、计算和优先级做一系列处理后,我们可以得到通过closure(maker)使maker.descriptions包含所有的约束描述,将每条描述再转换成Constraint类型(真实需要的约束)的约束信息,并返回[Constraint]类型

(2)对[Constraint]的每个Constraint执行 internal func activateIfNeeded(updatingExisting: Bool = false) 方法

通过NSLayoutConstraint的 open class func activate(_ constraints: [NSLayoutConstraint])让每个约束(即Constraint的layoutConstraints属性)激活

LayoutConstraint继承自UIKit.NSLayoutConstraint或者AppKit.NSLayoutConstraint

public final class Constraint { public var layoutConstraints: [LayoutConstraint] } public class LayoutConstraint : NSLayoutConstraint { public var label: String? { get { return self.identifier } set { self.identifier = newValue } } internal weak var constraint: Constraint? = nil } @available(macOS 10.10, *) open class func activate(_ constraints: [NSLayoutConstraint])

让LayoutConstraintItem(也就是对应的ConstraintView)通过internal func add(constraints: [Constraint]) 方法将LayoutConstraintItem的constraintsSet添加上所有约束

其中constraintsSet是与LayoutConstraintItem相关联的

private var constraintsSet: NSMutableSet { let constraintsSet: NSMutableSet if 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

至此,SnapKit完成了约束的添加约束与对象关联,以方便对约束的更新。

发布人:612c****    IP:125.64.25.***     举报/删稿
展会推荐
让朕来说2句
评论
收藏
点赞
转发