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