背景
今年的WWDC2019上最令人激动的新技术无疑就是全新的UI框架SwiftUI了。对于苹果开发者来说,它为大家开启了一个美丽的新世界:
- 简洁的声明式语法。极致简洁易读,百行代码变十行。
- 统一苹果旗下所有平台。iOS,macOS,tvOS, watchOS。
- 实时预览,多设备同时预览。修改代码或预览都可以动态生效。
- 清晰的数据流管理。结合Combine框架实现响应式编程。
SwiftUI着实惊艳,但遗憾的是它要求iOS 13.0以上的系统。这一条约束让大家觉得它还很遥远,因为国内多数App还要适配iOS 9.0,甚至iOS 8.0。
开源声明式框架
其实,SwiftUI的核心在于声明式语法描述界面,而声明式语法并不是什么新鲜事物,著名的React和近来大热的Flutter都采用了声明式语法。笔者查找和比较了一些开源的声明式界面框架,希望找到一个SwiftUI的合适替补,让大家在自己现有的项目里也能拥有类似SwiftUI的开发体验。
Layout
国外大神出品,基于自定义的XML DSL语法,功能强大,完备,支持Live Reload,自带工具集。
TemplateKit
类React框架,基于自定义的XML DSL语法,支持Live Reload。
LayoutKit
LinkedIn出品,质量性能放心。
ComponentKit
Facebook出品,使用Objective-C++编写,异步高性能布局,支持组件回收重用,优化内存和滑动性能。
Render
类React框架,支持组件回收重用,优化内存和滑动性能。
Tokamak
类React框架。
Komponents 类React框架,声明式语法比较简洁,与SwiftUI较为接近。
Few
类React框架。
Lima
超轻量级的框架,语法上与SwiftUI最为接近。
方案选择
这些框架都是在苹果发布SwiftUI之前诞生的。比较完一圈以后,我不由得感叹,还是SwiftUI更优雅,更强大。这也不难理解,毕竟苹果是后来者,SwiftUI是汲取了业界最新理念,厚积薄发的超越之作。
至于哪个框架更值得推荐,我想应该是因人而异。熟悉React的朋友,可以选择类React框架;看重性能的朋友,可以选择两个大厂的框架;需要实时预览开发体验的朋友,可以考虑前两个框架。而从我个人来说,我选择『小而美』的Lima。
它的优势如下:
- 超轻量,源码十分简单易懂,方便自行扩展。
- 语法上与SwiftUI最为接近,简洁易懂。
- 完全基于UIKit现有控件,没有复杂的概念。
我们来做个比较,看下Lima和SwiftUI实现下面的界面,它们的代码各长得什么样:

SwiftUI
HStack {
VStack {
Text("★★★★★")
Text("5 stars")
}
VStack {
HStack {
Text("Avocado Toast").font(.title)
Spacer()
Image("20x20_avocado")
}
Text("Ingredients: Avocado, Almond Butter, Bread, Red Pepper Flakes").lineLimit(1)
}
}
Lima
LMRowView(spacing: 10,
LMColumnView(
UILabel(text: "★★★★★", textAlignment: .center),
UILabel(text: "5 stars", textAlignment: .center)
),
LMColumnView(
LMRowView(
UILabel(text: "Avocado Toast", font:.preferredFont(forTextStyle: .title1)),
LMSpacer(),
UIImageView(image: UIImage(named: "20x20_avocado"))
),
UILabel(text: "Ingredients: Avocado, Almond Butter, Bread, Red Pepper Flakes")
)
)
看起来是不是很像,而且还是我们熟悉的UIKit,简直完美了。
Lima介绍
适用性
建议不要通过pod集成Lima,因为podspec上的Swift version和Deployment Target并不准确。实际上它的适用性是挺广泛的,可以支持iOS 9.0以上系统,稍作修改,也能支持iOS 8.x。而且Swift 4.x和Swift 5.x都可以编译通过。 另外,由于源码里有Objective-C的代码,所以纯Swift的项目最好是集成编译好的Lima.framework。
框架说明
通过上面的例子我们也看到,Lima本身很简单,很容易上手。这点和Masonry很像,你甚至无需预先学习AutoLayout,只是通过看几个例子就可以开始使用Masonry了。Lima也是如此一目了然,你也无需去学习SwiftUI,花两个小时看一下项目首页上的那个说明文档,跑一下Demo,基本就能开始编码了。
这里,我给大家简单理一下Lima的源码。
-
Lima是对UIKit的封装,内部通过AutoLayout实现布局。
-
LMLayoutView和LMBoxView是容器类的抽象基类,并不直接调用。 -
LMRowView,LMColumnView,LMAnchorView,LMRootView是四个容器类。LMRowView是水平容器;LMColumnView是垂直容器;LMAnchorView用于将子视图排布在它的边缘;LMRootView通常用于控制器的根视图,绕过系统定义的视图Margins。
-
LMSpacer默认是当做空白,同时也可以当做分隔线。LMSpacer(height: 0.5, backgroundColor: UIColor.gray) -
LMScrollView,LMTableViewCell,LMTableViewHeaderFooterView,LMCollectionViewCell是UIKit相应类型的子类。它们都有一个content属性,用于接收内容视图,而且它们的大小自动由content视图决定。 -
对于常用的UIKit控件和上述Lima定义的类型,Lima都在扩展中给它们定义了便利构造器,我们在声明式语法中需要通过这些构造器声明控件。
-
每个构造器的最后一个参数(with)都是一个参数为自身的闭包。这让我们可以在尾随闭包里获取控件的实例,并设置更多的属性。
var detailView: LMColumnView! LMColumnView(margin: 8, verticalAlignment: .top, spacing: 0) { self.detailView = $0 $0.clipsToBounds = true } -
除了构造器,我们还需要关注头文件里定义的属性。视图的排布是通过设置这些属性实现的。
My Fork
建议有兴趣尝试Lima的朋友,使用笔者fork的源码。
我这份源码修改了一个可能的同名属性覆盖问题,并且对原始代码做了一些扩展。
-
Lima在UIView的分类里定义了width和height属性。这两个属性名很容易导致同名属性覆盖的问题。我们常用的YYKit里就定义了这两个属性。所以,我给这两个属性加了lm_前缀。
-
我给
LMColumnView,LMRowView和LMAnchorView加了一个可以接收子视图数组的构造器,这样可以实现如下的调用:let items = ["First", "Second", "Third", "Four", "Five"] LMColumnView( items.map { item in UILabel(text: "Hello, \(item)!", textAlignment: .center) } ) -
我给
UIButton和UITextField的构造器里加入了事件处理参数,方便调用:UIButton(title: "Press Me!", action: { [weak self] btn in self?.showGreeting() }) -
我添加了一个
LMTableView类,期望实现SwiftUI里简单的List效果。SwiftUI
List(landmarks) { landmark in HStack { Image(landmark.thumbnail) Text(landmark.name) Spacer() if landmark.isFavorite { Image(systemName: "star.fill") .foregroundColor(.yellow) } } }Lima
LMTableView(items, { item in LMTableViewCell( LMRowView( UILabel(text: "Hello, \(item)!"), LMSpacer(), UIImageView(image: UIImage(named: "EmailIcon")) { if item == "Five" { $0.isDisplayable = false } else { $0.isDisplayable = true } } ) ) })不过,我的实现Cell没有做到重用,只适用于短列表。也欢迎大家继续研究。
不足
Lima用简单的代码实现了接近SwiftUI的简洁的声明式语法。但是,它只能看做是一个简化版的SwiftUI,远没有SwiftUI强大。
- 没有SwiftUI那样漂亮的状态和数据流实现。
- 没有SwiftUI那样强大简洁的列表实现。
- 没有SwiftUI那样的动画和绘图实现。
- 容器类会引入多余的视图层级,不太适合需要性能或内存利用率的场景。
总结
SwiftUI很强大,很美丽,但是我们还用不起来它。所以,笔者希望找一个开源的声明式UI框架,在现有的项目里引入声明式编程。笔者找到了Lima,并扩展了它。如果,你现在正在用Masonry布局UI,我建议您不妨试试Lima,因为声明式UI是未来的趋势,而且Lima和Masonry一样简单易用,Lima比Masonry的代码更加简洁易读。