iOS 目前只有手动布局和自动布局两种方式,flexbox 布局只能引入第三方库进行支持
这个布局的思想是来自 CSS 里的 flexbox,关于 CSS Flexbox 可以参考这篇文章 https://css-tricks.com/snippets/css/a-guide-to-flexbox/
目前应用比较广泛的 flexbox 布局库是 Facebook 出品的 yoga,国内有些大厂的布局引擎也是基于它来实现的。
yoga 是 C++ 实现的布局引擎。给 iOS 使用的是 YogaKit ,是官方封装的基于 UIView 的接口。
YogaKit 集成到项目里需要添加以下设置:
- Other Linker Flags:
-ObjC
- Link Binary With Libraries:
libc++.tbd
如果使用过 Masonry 或者 SnapKit,yoga 使用起来会感到很熟悉。
它们的 API 设计很相似。
YogaKit 把 yoga 引擎计算后的结果转换成 UIView 的坐标,在 UIView 的 layoutSubviews 方法里设置位置和大小。
本质是手动布局,所以布局速度很快,当然手动布局的缺点它也有:
- 不能由子 View 的大小推导出父容器的大小
- 大小改变了需要手动刷新布局
- 处理 safeArea 没有自动布局那么灵活
yoga 适合用在类似新浪微博那样的复杂组合中,可以极大的减少开发的时间。
以下演示 YogaKit 的使用,把 4 个 View 间距平分居中显示在父容器中
这个需求,手动布局和自动布局都不好实现,而在 Flexbox 中就是小菜一碟。
应用 flexbox 思想,这个布局很简单,row 方向布局,居中对齐就可以,代码也很简单,在父容器中:
override init(frame: CGRect) {
super.init(frame: frame)
// 设置子容器的排列方式
configureLayout { (layout) in
layout.isEnabled = true
layout.flexDirection = .row
layout.alignItems = .center
layout.justifyContent = .spaceEvenly
}
let v1 = UIView()
v1.backgroundColor = .blue
addSubview(v1)
v1.configureLayout { (layout) in
layout.isEnabled = true
layout.width = 50
layout.height = 50
}
let v2 = UIView()
v2.backgroundColor = .red
addSubview(v2)
v2.configureLayout { (layout) in
layout.isEnabled = true
layout.width = 50
layout.height = 50
}
let v3 = UIView()
v3.backgroundColor = .purple
addSubview(v3)
v3.configureLayout { (layout) in
layout.isEnabled = true
layout.width = 50
layout.height = 50
}
let v4 = UIView()
v4.backgroundColor = .orange
addSubview(v4)
v4.configureLayout { (layout) in
layout.isEnabled = true
layout.width = 50
layout.height = 50
}
yoga.applyLayout(preservingOrigin: true)
}
override func layoutSubviews() {
super.layoutSubviews()
yoga.applyLayout(preservingOrigin: true)
}
layout.isEnabled = true
是必须要的,告诉 yoga 引擎要应用 flexbox 布局。
以下两行代码是实现布局的关键,分别指定居中和间隔,和 CSS 里的参数设置一样:
layout.alignItems = .center
justifyContent = .spaceEvenly
配置好了以后需要调用 yoga.applyLayout(preservingOrigin: true)
进行布局计算和坐标设置。
因为 YogaKit 是手动布局,所以在实际的应用中可以和结合系统的自动布局一起使用。
官方的 YogaKit 是直接设置 UIView 的 frame,这种方式一般没有问题,但如果设置了 transform 就会导致布局异常,正确的方式应该是使用 bounds 和 center 代替设置 frame。
对此我已经提交了相关的 PR:yoga/pull/913
多理解一种布局思想,实现页面效果更加得心应手。
(完)