Swift 纯代码布局框架 SnapKit 入门教程

进行iOS UI开发的时候,主要的布局方式有xib、storyboard和纯代码布局。storyboard布局有个致命的问题就是,如果多人同时进行开发很容易出现代码冲突问题,而且比较负责而且容易变动的布局也不好处理;纯代码布局,苹果为我们提供的适配框架有:VFL、UIViewAutoresizing、Auto Layout、Size Classes等,除以之外,也有很多第三方开源的布局,比如MasonrySnapKitSDAutoLayoutCartographyPureLayoutNeon

SnapKit

每个框架都有自己的特点,开发者可以根据自己的喜欢选择,笔者是使用Swift编写,选择了最多人选择的布局框架SnapKit进行开发,选择SnapKit主要有几个原因,第一是最多人使用,二是还是维护更新,三是语法简单。

cocoapods 依赖
    pod 'SnapKit', '~> 5.0.0'
居中显示

下面的案例是创建一个box的View,设置宽高为100,在父View中居中显示。

import UIKit
import SnapKit
class FirstViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        let box = UIView()
        box.backgroundColor = UIColor.blue
        self.view.addSubview(box)
        box.snp.makeConstraints { (make) in
            // 宽高设置为100
            make.size.equalTo(100)
            // 在父视图居中显示
            make.center.equalToSuperview()
        }
    }
}

dividedBy、multipliedBy

dividedBy、multipliedBy两者的用法都是一样的,dividedBy(x)是除以x,multipliedBy(x)是乘以x。

class FirstViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        let box = UIView()
        box.backgroundColor = UIColor.blue
        self.view.addSubview(box)
        box.snp.makeConstraints { (make) in
            // 在父视图居中显示
            make.center.equalToSuperview()
            // 宽度等于父视图宽度的1/2
            make.width.equalToSuperview().dividedBy(2)
            // 高度等于父视图高度的0.25
            make.height.equalToSuperview().multipliedBy(0.25)
        }
    }
}
offset (偏移量,位移)
class FirstViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        let box = UIView()
        box.backgroundColor = UIColor.blue
        self.view.addSubview(box)
        box.snp.makeConstraints { (make) in
            // 距离父视图上边50dp
            make.top.equalToSuperview().offset(100)
            // 距离父视图左边50dp
            make.left.equalToSuperview().offset(50)
            // 距离父视图下边50dp
            make.bottom.equalToSuperview().offset(-100)
            // 距离父视图右边50dp
            make.right.equalToSuperview().offset(-50)
            // 更加简单的写法,下面一行代码的效果和上面4行代码一样
            // make.edges.equalToSuperview().inset(UIEdgeInsets(top: 100,left: 50,bottom: 100,right: 50))
        }
    }
}
topLayoutGuide、bottomLayoutGuide、safeAreaLayoutGuide

topLayoutGuide、bottomLayoutGuide在iOS 11.0 被弃用了,但是这里还是介绍一下这两者的用法,之所以被废弃是因为iPhone X之后推出的SafeArea概念,所以建议使用 safeAreaLayoutGuide。

class FirstViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        let box1 = UIView()
        box1.backgroundColor = UIColor.blue
        self.view.addSubview(box1)
        box1.snp.makeConstraints { (make) in
            make.size.equalTo(100)
        }
        
        let box2 = UIView()
        box2.backgroundColor = UIColor.red
        self.view.addSubview(box2)
        box2.snp.makeConstraints { (make) in
            make.size.equalTo(100)
            make.right.equalToSuperview().offset(0)
            // bottomLayoutGuide 已经被弃用
            // make.top.equalTo(topLayoutGuide.snp.bottom)
            // 建议使用 safeAreaLayoutGuide
            make.top.equalTo(self.view.safeAreaLayoutGuide.snp.top)
            
        }
        
        let box3 = UIView()
        box3.backgroundColor = UIColor.yellow
        self.view.addSubview(box3)
        box3.snp.makeConstraints { (make) in
            make.size.equalTo(100)
            make.bottom.equalToSuperview().offset(0)
        }

        let box4 = UIView()
        box4.backgroundColor = UIColor.black
        self.view.addSubview(box4)
        box4.snp.makeConstraints { (make) in
            make.size.equalTo(100)
            make.right.equalToSuperview().offset(0)
            // bottomLayoutGuide 已经被弃用
            // make.bottom.equalTo(bottomLayoutGuide.snp.top)
            // 建议使用 safeAreaLayoutGuide
            make.bottom.equalTo(self.view.safeAreaLayoutGuide.snp.bottom)
        }
    }
}
priority 优先级用法

SnapKit一共提供了4种优先级,优先级顺序是:required > high > medium > low。

class FirstViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        let box1 = UIView()
        box1.backgroundColor = UIColor.blue
        self.view.addSubview(box1)
        box1.snp.makeConstraints { (make) in
            // 父视图居中的优先级是medium,距离父视图右边10的优先级是high,距离父视图上边10的优先级是low,所以运行的结果是“垂直居中,距离父视图右边10”
            make.center.equalToSuperview().priority(.medium)
            make.right.equalToSuperview().offset(10).priority(.high)
            make.top.equalToSuperview().offset(10).priority(.low)
            // 运行结果是宽高等于100
            make.size.equalTo(100).priority(.medium)
            make.size.equalTo(200).priority(.low)
        }
    }
}
image.png
equalTo、lessThanOrEqualTo、greaterThanOrEqualTo

配合priority可以实现一些特别的效果。

  • equalTo 是等于
  • lessThanOrEqualTo 小于或等于
  • greaterThanOrEqualTo 大于或等于
    效果一
class FirstViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        let box1 = UIView()
        box1.backgroundColor = UIColor.blue
        self.view.addSubview(box1)
        
        let box2 = UIView()
        box2.backgroundColor = UIColor.red
        self.view.addSubview(box2)
        
        box1.snp.makeConstraints { (make) in
            make.left.equalToSuperview().offset(0)
            make.size.equalTo(100).priority(.high)
            // box1的宽高大于或等于box1宽高
            make.size.greaterThanOrEqualTo(box2).priority(.high)
        }
        box2.snp.makeConstraints { (make) in
            make.right.equalToSuperview().offset(0)
            make.size.equalTo(200).priority(.low)
        }
        let label = UILabel()
        label.text = "效果一"
        self.view.addSubview(label)
        label.snp.makeConstraints { (make) in
            make.center.equalToSuperview()
        }
    }
}

效果二

class FirstViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        let box1 = UIView()
        box1.backgroundColor = UIColor.blue
        self.view.addSubview(box1)
        
        let box2 = UIView()
        box2.backgroundColor = UIColor.red
        self.view.addSubview(box2)
        
        box1.snp.makeConstraints { (make) in
            make.left.equalToSuperview().offset(0)
            make.size.equalTo(100).priority(.low)
            // box1的宽高大于或等于box1宽高
            make.size.greaterThanOrEqualTo(box2).priority(.high)
        }
        box2.snp.makeConstraints { (make) in
            make.right.equalToSuperview().offset(0)
            make.size.equalTo(200).priority(.high)
        }
        let label = UILabel()
        label.text = "效果二"
        self.view.addSubview(label)
        label.snp.makeConstraints { (make) in
            make.center.equalToSuperview()
        }
    }
}

效果三

class FirstViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        let box1 = UIView()
        box1.backgroundColor = UIColor.blue
        self.view.addSubview(box1)
        
        let box2 = UIView()
        box2.backgroundColor = UIColor.red
        self.view.addSubview(box2)
        
        box1.snp.makeConstraints { (make) in
            make.left.equalToSuperview().offset(0)
            make.size.equalTo(100).priority(.high)
            // box1的宽高大于或等于box1宽高
            make.size.greaterThanOrEqualTo(box2).priority(.low)
        }
        box2.snp.makeConstraints { (make) in
            make.right.equalToSuperview().offset(0)
            make.size.equalTo(200).priority(.high)
        }
        let label = UILabel()
        label.text = "效果三"
        self.view.addSubview(label)
        label.snp.makeConstraints { (make) in
            make.center.equalToSuperview()
        }
    }
}
总结

从上述的例子可以看到,使用SnapKit框架进行纯代码布局还是非常的简单,而且SnapKit还一直在维护更新,非常推荐使用,基于以上的简单的API就可以完成非常复杂的UI布局。

推荐阅读更多精彩内容