首页投稿(暂停使用,暂停投稿)iOS Developer

Swift学习->自己动手写自动布局框架

2017-06-23  本文已影响171人  CoderFM

前言

最近公司项目不忙, 在学swift, 虽然之前也了解过, 但是没怎么写, 所以基本上都忘光了, 作为一个程序员, 怎么能够不追求新的艺术呢, 对, swift就是一门艺术, 一个礼拜左右的时间, 边看边练, 写了三个文件

下面针对自动布局的简单概述一下
功能没有那么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

举个例子

  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地址

上一篇下一篇

猜你喜欢

热点阅读