Swift学习->自己动手写自动布局框架
2017-06-23 本文已影响171人
CoderFM
前言
最近公司项目不忙, 在学swift, 虽然之前也了解过, 但是没怎么写, 所以基本上都忘光了, 作为一个程序员, 怎么能够不追求新的艺术呢, 对, swift就是一门艺术, 一个礼拜左右的时间, 边看边练, 写了三个文件
- 网络请求(对URLSession的封装)
- 图片加载器(简仿写的SDWebImage)
- 自动布局(类似Massory的调用方法 基于->NSLayoutConst.....)
下面针对自动布局的简单概述一下
功能没有那么SnapKit那么强大, 对我而言, 够用足矣
调用的方法如下
imageView.makeConstraint { (maker) in
maker.center.equalTo(self.view.cm_center)
maker.width.height.equalTo(self.view).multiplier(0.5)
}
button.makeConstraint { (maker) in
maker.centerX.equalTo(self.view)
maker.top.equalTo(imageView.cm_bottom).constant(30)
maker.height.equal(40)
maker.width.equalTo(imageView)
}
上面代码的效果如下
跟我想的结果一样.png差不多, 挺好的 , 嗯 , 很棒
当然了. 更新约束也有的, 就不演示了只不过是make换成了update 比较习惯了Massory的 所以就采用了相同的办法
实现的Api基于分类(extension) 对UIView的extension
func makeConstraint(constraint: (FMConstraintMaker) -> ()) -> Void {
self.isUpdateConstraint = false //是否是更新布局的标识
constraint(FMConstraintMaker(currentView: self))
}
image.png
- FMConstraintMaker(生成FMCMAttributes一个)
- FMCMAttributes(保存要约束的属性FMCMAttribute)
- FMCMACalculate(根据属性, 加上判断得出NSLayoutConstraint)
- FMCMATarget(相对谁的约束包含属性)
- FMCMAttribute(枚举类型 所有的属性类型)
- FMCMRelation(枚举类型 约束的关系类型)
- FMCMVRelation(枚举类型 当前控件与目标的控件的关系)
举个例子
button.makeConstraint { (maker) in
maker.left.right.top.bottom.equalTo(self.view) // 四边约束紧靠view的四边
}
// maker.left之后
// 其他的属性都是一样的 根据不同的属性名传入相应的约束类型, 返回一个FMCMAttributes
lazy var left: FMCMAttributes = {
return FMCMAttributes(attribute: .left, current: self.currentMakerView)
}()
// maker.left.right之后 .top.bottom 也是一样的道理
/* 这里调用的就是FMCMAttributes的对象了 仅仅是将对应的
属性名标记加入到数组里 留着给后面去生成对应的布局 */
lazy var right: FMCMAttributes = {
self.attributes.append(.right)
return self
}()
再然后就到了equalTo这个方法 这个根据传进来的参数做判断生成FMCMACalculate
@discardableResult //这个标识是忽略这个方法的返回值没有调用的警告
func equalTo(_ target: Any) -> FMCMACalculate {
return self.toCalculate(target: target, relation: .Equal)
}
private func toCalculate(target: Any, relation: FMCMRelation) -> FMCMACalculate {
if let targetView = target as? UIView {
return FMCMACalculate(current: self, targetView: targetView, relation: relation)
}
if let targetAttr = target as? FMCMATarget {
return FMCMACalculate(current: self, target: targetAttr, relation: relation)
}
return FMCMACalculate(current: self, relation: relation)
}
根据上传的传入的参数判断
// FMCMACalculate
init(current: FMCMAttributes, targetView: UIView?, relation: FMCMRelation) {
self.current = current // FMCMAttributes实例对象
self.targetView = targetView // 目标的控件
self.hasAttribute = false // 是否是带有属性的 如:self.view.cm_left
self.target = nil // 带有属性的目标
self.relation = relation // 约束的关系 相等大于小于
}
根据上面的代码 到这里已经结束了
但是这并没有去添加布局的代码
添加布局的代码在
var constant: (Float) -> Void {
return { (value) in
self.hasConstant = true
for attr in self.current.attributes {
self.setAttribute(attribute: attr, constant: value)
}
}
}
func setAttribute(attribute: FMCMAttribute, constant: Float) -> Void {
let cons: NSLayoutConstraint = self.layoutConstraint(attribute: attribute, constant: constant) // 根据属性与constant生成一个NSLayoutConstraint作为
var change: UIView?
switch self.viewrelation() { // 获取当前的控件与目标的控件的关系
case .FatherAndSon:
change = self.current.current
break
case .SonAndFather:
change = self.current.current.superview!
break
case .Equative:
change = self.current.current.superview!
break
case .BeMySelf:
change = self.current.current
break
default:
change = nil
break
}
if change != nil {
if self.current.current.isUpdateConstraint { // 更新约束
let consItems = change!.constraints
if consItems.count == 0 {
return
}
var changeCon: NSLayoutConstraint?
for con in consItems {
if con.firstItem as! NSObject == self.current.current && con.firstAttribute == attribute.ConstraintAttr() {
changeCon = con
break
} else {
continue
}
}
if changeCon != nil {// 如果找到旧的约束 就可以移除这个旧的约束 更新成当前最新的
change!.removeConstraint(changeCon!)
change!.addConstraint(cons)
}
} else { // 添加约束
change!.addConstraint(cons)
}
}
}
到这里已经差不多了 至于为什么没有调用这个方法就去更新是因为在析构方法里加了一层判断 当这个FMCMACalculate对象即将销毁的时候 如果没有去布局的 就去执行布局
deinit {
if !self.hasConstant {
self.constant(0)
}
}
差不多就是这些了 想通了 实现起来还是挺简单的
更多请看源文件
demo地址