博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
snapkit equalto和multipliedby方法
阅读量:4881 次
发布时间:2019-06-11

本文共 12927 字,大约阅读时间需要 43 分钟。

最近在使用snapkit过程中遇到,在github上搜索之后发现另外

frameImageContainer.snp.makeConstraints({ (make) in     make.width.equalTo(295).multipliedBy(0.2)     make.height.equalTo(355).multipliedBy(0.2)     make.top.equalToSuperview().offset(self.view.frame.height/8)     make.centerX.equalToSuperview(); })

看起来很理所当然的,明显不可以这样写,但是具体是什么原因呢,明明没有报任何错误和警告,但是.multipliedBy()方法却没有效果,那我们来看一下snapkit源码。

1.首先点进equalTo()方法,代码是这样的:

@discardableResult    public func equalTo(_ other: ConstraintRelatableTarget, _ file: String = #file, _ line: UInt = #line) -> ConstraintMakerEditable {        return self.relatedTo(other, relation: .equal, file: file, line: line)    }

再点进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    }

可以看到上面红色部分,此时other可以转换为ConstraintConstantTarget类型,设置related的target为nil,attributes为none,constant设置为other,最后将这些变量赋值给description属性保存。

2.multipliedBy()方法:

@discardableResult    public func multipliedBy(_ amount: ConstraintMultiplierTarget) -> ConstraintMakerEditable {        self.description.multiplier = amount        return self    }

可以看到,multipliedBy方法中只是简单的赋值,将amout值复制到description中保存。

3.再来看一下makeConstraints()方法:

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)        }    }

首先将要添加约束的对象封装成ConstraintMaker对象,再把它作为参数调用closure,开始添加约束。closure中的每条语句会先被解析ConstraintDescription对象,添加到maker的descriptions属性中。 在第一个for循环中可以看到,每次循环从descriptions中取出一条description,判断是否改description是否有constraint属性;

constraint属性使用的懒加载,值为:

internal 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        )    }()

先判断relation,related,sourceLocation的值是否为nil,若为nil则返回nil,否则就根据description属性创建一个Constraint对象并返回。

Constraint的构造方法:

internal init(from: ConstraintItem,                  to: ConstraintItem,                  relation: ConstraintRelation,                  sourceLocation: (String, UInt),                  label: String?,                  multiplier: ConstraintMultiplierTarget,                  constant: ConstraintConstantTarget,                  priority: ConstraintPriorityTarget) {        self.from = from        self.to = to        self.relation = relation        self.sourceLocation = sourceLocation        self.label = label        self.multiplier = multiplier        self.constant = constant        self.priority = priority        self.layoutConstraints = []        // get attributes        let layoutFromAttributes = self.from.attributes.layoutAttributes        let layoutToAttributes = self.to.attributes.layoutAttributes        // get layout from        let layoutFrom = self.from.layoutConstraintItem!        // get relation        let layoutRelation = self.relation.layoutRelation        for layoutFromAttribute in layoutFromAttributes {            // get layout to attribute            let 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 = .leftMargin                        case .right:                            layoutToAttribute = .rightMargin                        case .top:                            layoutToAttribute = .topMargin                        case .bottom:                            layoutToAttribute = .bottomMargin                        default:                            fatalError()                        }                    } else if self.from.attributes == .margins && self.to.attributes == .edges {                        switch layoutFromAttribute {                        case .leftMargin:                            layoutToAttribute = .left                        case .rightMargin:                            layoutToAttribute = .right                        case .topMargin:                            layoutToAttribute = .top                        case .bottomMargin:                            layoutToAttribute = .bottom                        default:                            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                    }                }            #else                if self.from.attributes == self.to.attributes {                    layoutToAttribute = layoutFromAttribute                } else if layoutToAttributes.count > 0 {                    layoutToAttribute = layoutToAttributes[0]                } else {                    layoutToAttribute = layoutFromAttribute                }            #endif            // get layout constant            let layoutConstant: CGFloat = self.constant.constraintConstantTargetValueFor(layoutAttribute: layoutToAttribute)            // get layout to            var layoutTo: AnyObject? = self.to.target            // use superview if possible            if layoutTo == nil && layoutToAttribute != .width && layoutToAttribute != .height {                layoutTo = layoutFrom.superview            }            // create layout constraint            let layoutConstraint = LayoutConstraint(                item: layoutFrom,                attribute: layoutFromAttribute,                relatedBy: layoutRelation,                toItem: layoutTo,                attribute: layoutToAttribute,                multiplier: self.multiplier.constraintMultiplierTargetValue,                constant: layoutConstant            )            // set label            layoutConstraint.label = self.label            // set priority            layoutConstraint.priority = LayoutPriority(rawValue: self.priority.constraintPriorityTargetValue)            // set constraint            layoutConstraint.constraint = self            // append            self.layoutConstraints.append(layoutConstraint)        }    }

重点看红色部分,遍历layoutAttributes,并根据layoutAttribute的值生成一个LayoutConstraint对象添加到layoutConstraints数组中。LayoutConstraint继承自系统类NSLayoutConstraint。

4.最后再看3中的第二个for循环,使用activeIfNeeded()方法激活约束:

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.layoutConstraints        if 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.secondAttribute                updateLayoutConstraint.constant = self.constant.constraintConstantTargetValueFor(layoutAttribute: updateLayoutAttribute)            }        } else {            NSLayoutConstraint.activate(layoutConstraints)            item.add(constraints: [self])        }    }

当updatingExisting为false时,进入else语句,使用的系统类NSLayoutConstraint的方法激活约束:

/* Convenience method that activates each constraint in the contained array, in the same manner as setting active=YES. This is often more efficient than activating each constraint individually. */    @available(iOS 8.0, *)    open class func activate(_ constraints: [NSLayoutConstraint])

并将设置过的约束添加到item的constraintSet这个私有属性中:

internal var constraints: [Constraint] {        return self.constraintsSet.allObjects as! [Constraint]    }        internal func add(constraints: [Constraint]) {        let constraintsSet = self.constraintsSet        for constraint in constraints {            constraintsSet.add(constraint)        }    }        internal func remove(constraints: [Constraint]) {        let constraintsSet = self.constraintsSet        for constraint in constraints {            constraintsSet.remove(constraint)        }    }        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            }

5.通过这个过程不难发现,使用make.width.equalTo(295).multipliedBy(0.2) 这种方式不能得到想要的结果。在3中Constraint的构造方法的红色部分,其实构造LayoutConstraint对象时调用的NSLayoutConstraint的便利构造器方法:

/* Create constraints explicitly.  Constraints are of the form "view1.attr1 = view2.attr2 * multiplier + constant"      If your equation does not have a second view and attribute, use nil and NSLayoutAttributeNotAnAttribute.     */    public convenience init(item view1: Any, attribute attr1: NSLayoutConstraint.Attribute, relatedBy relation: NSLayoutConstraint.Relation, toItem view2: Any?, attribute attr2: NSLayoutConstraint.Attribute, multiplier: CGFloat, constant c: CGFloat)

注意上面注释 view1.attr1 = view2.attr2 * multiplier + constant 如果只设置为数字,则相当于view2为nil,所以view1的属性值只能等于constant的值,不会乘以multiplier。

6.终于写完了,哈哈。 ,对应的方法已打断点,可以跟着代码一步步调试,有助于理解。

 

 

疑问:在4中最后一部分红色字体的内容,私有属性constraintsSet为啥不直接使用,还要使用runtime给当前对象绑定一个同名的属性,每次使用时获取绑定的属性的值,不懂,希望知道的同学不吝赐教。

 

 

 

转载于:https://www.cnblogs.com/shenyuiOS/p/9706832.html

你可能感兴趣的文章
hdu 5412 CRB and Queries(整体二分)
查看>>
CentOS如何安装linux桌面?
查看>>
Speech and Booth Demo in Maker Faire Shenzhen 2018
查看>>
bzoj 1670: [Usaco2006 Oct]Building the Moat护城河的挖掘
查看>>
bzoj 2281: [Sdoi2011]黑白棋
查看>>
bzoj 4475: [Jsoi2015]子集选取
查看>>
团队开发7
查看>>
java之静态代理与动态代理
查看>>
软件测试2019:第四次作业
查看>>
201571030335 + 小学四则运算练习软件项目报告
查看>>
不用代码就能实现get与post
查看>>
gdb基本调试命令
查看>>
互联网开放平台API安全设计
查看>>
OPMN
查看>>
LOG收集系统(一):原日志至收集
查看>>
【文摘】经营十二条
查看>>
清除浮动的方法
查看>>
Logstash连接Elasticsearch异常
查看>>
洛谷P4287 [SHOI2011]双倍回文(回文自动机)
查看>>
用户交互程序,格式化输出
查看>>