在 iOS 里使用 Flexbox 布局

Dec 08, 2019 • 预计阅读时间 2 分钟

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

多理解一种布局思想,实现页面效果更加得心应手。

(完)

iOS

给 Hugo 开启 Disqus 评论系统

在 Hugo 的 Markdown 里直接使用 HTML

comments powered by Disqus