相信每个见到 SwiftUI 的开发者,都会马上将这门船新的 UI 框架和 Flutter 联系到一块儿。是的,它们身上有太多太多类似的地方,类似的声明语法、实时热更新、跨平台(SwiftUI 仅仅跨 Apple 平台)等等,让羡慕了前端技术爆发的移动开发圈子也热闹了一回。那么 SwiftUI 和 Flutter 到底有什么类似和不一样?它们又各有什么优缺点?以及最后,单就技术方向而言,谁才是将来跨平台方案的赢家呢?前端
现代计算机语言愈加趋于类似是一个不争的事实,由于每一个语言的基本目标都至关的一致:简洁、灵活、安全、高性能。同时,各类语言的优异特性也都在被相互借鉴,更近一步减小了各个语言之间的鸿沟。git
Swift 和 Dart 分别做为这两个 UI 框架的惟一官方语言,在语法层面的差异其实很是小,网上也有大量的文章来对比这两种语言的优劣性。个人使用体验和大部分人类似,就目前而言,Swift 和 Dart 各有优劣。程序员
Swift 比 Dart 更加简洁。Swift 自己在语法层面已经比 Dart 要简洁不少,好比无需在句末添加;
分号等。这一点在直接编写 SwiftUI 和 Flutter 上会显得尤其明显。不过这个问题并不全是语言层面带来的问题,也跟这两个 UI 框架的设计有关。github
ForEach(userData.landmarks) { landmark in
NavigationButton( destination: LandmarkDetail(landmark: landmark)) {
LandmarkRow(landmark: landmark)
}
}
复制代码
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
final landmark = landmarks[index];
return LandmarkCell(
landmark: landmark,
onTap: () {
Navigator.push(
context,
CupertinoPageRoute(
builder: (context) => LandmarkDetail(
landmark: landmark,
),
),
);
},
);
},
childCount: landmarks.length,
),
),
复制代码
二者都实现了一个能够点击的列表页,都调用了一个自定义 Cell 。swift
Swift 比 Dart 更加严格,从某种角度上讲会更加安全,固然安全这种事情追根揭底仍是得靠人。后端
Swift 在发布的第5个年头居然尚未 await/async ,虽然早有提案但一直没有落实下来。不过 Dart 这边也没有可选型。只能说二者互相吸取的还不够。安全
Dart 的黑科技:同时支持 AOT 和 JIT,这也赋予了 Flutter Web 级的热重载特性,是 Flutter 对决 SwiftUI 的有力武器。框架
Swift 和 Dart 都是开源语言。Apple 对 Swift 的开放是前所未有的,目前 Swift 已经能够脱离 macOS 运行,同时在社区中也出现了诸如后端框架 Vapor
等一系列有意思的东西,更是成为了 Tensorflow
的官方语言之一。这一切大大拓展了 Swift 的使用场景。不过,Dart 在可玩性上明显走的要比 Swift 更快。 用其开发的后端框架已经被一些公司投入到了生产环境,Google 的 Web 框架 AngularDart
(非 Flutter Web)也早已在一些服务上稳定运行了许久。更不用说借助 Flutter 跑遍 iOS Android macOS Windows Web,简直能够用所向睥睨来形容。less
一个语言的设计好坏的确会影响一个框架的受欢迎程度,我已经听过无数 Flutter 开发人员吐槽 Dark 无止境的嵌套问题。虽然 Dark 的可玩度更高,但就单 UI 框架而言, 我我的认为 Swift 的使用感觉要明显好于 Dart。async
SwiftUI 和 Flutter 都无一例外的使用了声明式语法和各自的 DSL 来描述 UI ,它的好处是,你能一眼就从代码的结构中看出实际 UI 的结构,而且避免反复的逻辑代码。SwiftUI 用数据绑定和状态管理替代了原有的复杂控制逻辑,而 DSL 又使得代码在结构上和 UI 的层级结构高度一致。
VStack {
MapView()
.edgesIgnoringSafeArea(.top)
.frame(height: 300)
CircleImage()
.offset(y: -130)
.padding(.bottom, -130)
VStack(alignment: .leading) {
Text("Turtle Rock")
.font(.title)
HStack(alignment: .top) {
Text("Joshua Tree National Park")
.font(.subheadline)
Spacer()
Text("California")
.font(.subheadline)
}
}
.padding()
Spacer()
}
复制代码
上面是 WWDC 上出现的一个 SwiftUI Demo LandMark。即使你没有学习过 SwiftUI 的语法,稍稍阅读一下上面的代码,你也能发现,它和图片中的 UI 是一一对应的。这就是声明式语法的优势。不过在具体写代码的时候,Flutter 和 Swift 的感觉就差太多了,最明显的就是:Flutter 要比 SwiftUI 复杂太多了。上面的效果用 Flutter 实现要写多少代码呢?多到我都无法粘贴过来,不然个人文章有一半都会是 Dart 代码。不过这里有一我的仿照着 Demo 写了一份 Flutter 版的 LandMarks ,你们能够参考一下。
为何一样是声明式语法,Fluter 要比 SwiftUI 复杂这么多呢。这其实体现了 Apple 和 Google 在设计这两个 UI 框架时的不一样思路,甚至表明了这两家公司的不一样文化。对 iOS 和 Android 稍有了解的开发者都知道,iOS 开发要比 Android 开发轻松不少。单就配置开发环境来讲,iOS 就能够比 Android 提前1天开始写代码。更不容说复杂的框架、混乱的平台兼容性、生涩难懂的文档。
Apple 对待开发者,就像是在对待新手同样,极尽所能的去简化开发过程,减小开发中的工做量,甚至还要让开发变成一件优雅的事情,这从 Xcode 的外观功能设计到系统 API 再到 Swift 语言都是如此。而 Google 在对待开发者时,更像是对待极客同样,将大量的工做和创做交给开发人员,让它们自由的创造有意思的东西。但相应的,它的门槛也很高,并且自由带来的碎片化和不可控性也会致使开发效率的下降。
在设计 SwiftUI 的 API 时,Apple 隐藏了大量的内部细节,让开发者调用尽量少的代码就能实现相应的效果。开发者不须要重写初始化方法,不须要使用 return
,不须要要关心 context
上下文,也不须要关心它应该是一个 StatefulWidget
仍是 StatelessWidget
, 更不须要关心用的是 Material Design 仍是 iOS Style。能作的 Apple 都帮你在内部作好了,你须要作的,只是告诉编译器,你须要什么控件,它们是什么样的,它们应该在什么地方,仅此而已。若是说 Flutter 给移动开发带来的声明式语法让全部人眼前一亮的话,那么 SwiftUI 的出现则是告诉全部人(包括 Flutter ),什么才是真正的声明式语法。
SwiftUI 最为重磅的功能,就是万众期待的热加载,也就是实时更新。Web 端超高的开发效率一直是移动端求之不得的技能,RN 的出现让全部移动开发者第一次看到了这种可能,虽然它现在也有着本身的问题。Flutter 做为 Web 技术的一种延伸,继承了大量的 Web 技术特性,热加载彷佛也就成为了手到擒来的技能。而 SwiftUI 要想实现近似 Web 那样的实时刷新,所要面对的困难要复杂的多。从个人实际体验下来,也充分说明了这一点。
SwiftUI 的实时预览分为静态预览和动态预览。默认状况下展现的是静态预览,它的速度快,并且支持代码和可视化两种编写方式。不过它没有任何响应事件,没法滚动和跳转页面。若是须要动态调试,则须要切换到动态预览。动态预览须要通过一段编译时间,而后能够以彻底动态的形式实时响应 UI 的变化,并且能够在真机上实时调试。但动态预览的限制还比较多,没法作到 Flutter 那种只需编译一次,以后整个 App 都能实现动态更新。
为何会有静态预览和动态预览呢?我相信若是不是出于一些问题的考虑(性能问题、难以处理的 Bug),Apple 是断然不会将这个问题复杂化的。只能说,目前残疾版的热加载是 Apple 能给咱们的最好效果。正所以,目前 SwiftUI 所谓的实时刷新,距离 Web 以及 Flutter,仍是小儿科。静态 UI 调试其实彻底能够经过 StoryBoard 来实现,而真正实用的动态预览又有必定的局限性。不过 SwiftUI 尚处在测试阶段,暂时还没法对其性能做出最终的定论。
Google 宣称 Flutter 的目标之一就是流畅(60Hz),目前来看,它确实在大部分场景作到了。不过根据个人测试(此时尚处于 beta 版),像页面转场、控件动画这样的场景,跑到 60 Hz 仍是有必定压力。我一直都有在关注 Flutter 项目,咱们内部也在尝试将部分模块使用 Flutter 重写。但我至今体验下来的状况,目前的 Flutter, 只能说很接近原平生台的流畅度。不过即使 Flutter 可以达到系统原生的流畅度,它也始终没法回避性能损耗的问题。iOS 的原生 UI 框架(包括 SwiftUI)都是搭建在 Metal 之上的,Metal 对于 GPU 的使用效率远要高于 OpenGL 。其带来的结果在个人测试中也代表了,一样的页面渲染和动画,Flutter 对于 CPU 和 GPU 的利用率要高于 iOS 原生 UI 框架。而高性能消耗,对设备就意味着高发热和低续航。这在移动设备上尤其严重。
反观 SwiftIU,Apple 彷佛照搬了系统全部的原生控件,原则上不会有性能上的改观,但千万不要被你的眼睛骗了,Apple 在你看不见的地方,下了一盘大棋。
上面两张图分别是 SwiftUI 和 iOS原生渲染的 Label,它们都运行在 iOS 平台,都长得一摸同样,但它们背后是两个彻底不同的东西。咱们发现,SwiftUI 中的文字已经不是 iOS 系统的原生控件了,而是一个新的类型,名为 Text
。它实际上是一个继承自 UIView 的新控件,全名是 DisplayList.ViewUpdater.Platform.CGDrawingView 。观察右侧的属性窗口,发现两个控件有着彻底不一样的属性。UILabel
有着复杂的属性,用于控制样式、交互等。而 Text
则要精简的多的多。
有传言说,SwiftUI 的部分控件已经抛弃了系统原生 UI 框架,转而直接使用了更加底层,同时也在 Apple 生态系统中更加通用的 Core Animation、Core Graphics、Core Text 进行渲染。是否直接使用了底层框架进行高效渲染我暂时还无得而知,但目前能够确定的是,Apple 正在将 SwiftUI 剥离出原有的 UI 框架,丢掉沉重的历史包袱。这势必会带来诸多益处,好比更高的自由度、更好的性能,以及真正意义上的:跨平台。
可能不少人都会说,SwiftUI 和 Flutter 根本无法一块儿比较,SwiftUI 不是真正的跨平台。没错,SwiftUI 目前所跨的仅仅是 Apple 自家的生态圈,包含了 iOS(iPadOS)macOS watchOS tvOS,而隔壁 Flutter 早已玩腻了移动端,开始向 Windows macOS 以及 Web进发。不过,你不能否认,SwiftUI 确实作到了在彻底不一样的系统平台上实现了 Write once,use anywhere。并且,隐藏在这背后的思路还彻底不同。
SwiftUI 从根本上不是在构建 UI,而是在描述 UI 。这二者有什么区别?构建 UI 时,你会明确每个控件的类型,甚至精确到平台。好比在原生 UI 框架中,你须要一个输入框 ,你就要根据不一样的平台,选择具体是使用 UITextFiled(iOS) 仍是 NSTextFiled(macOS),而这两个输入框 有着不一样的属性、外观和特性。在 Flutter 中,你不须要考虑不一样平台的控件类型,但你须要考虑控件的风格,官方提供了符合 Material Design 的 Android 控件和 iOS Style 的 iOS 控件,它们的许多特性也不一致,这一切给 UI 构建带来了更加复杂的逻辑和工做量。
而 SwiftUI 更像是 Web 的模版,它只描述 UI ,而控件具体长什么样子,有什么特性,会根据编译的平台因地制宜的实现,并且是自动的。这就像是给 Web 套上了一个模版,一个主题。
左侧的代码描述了一个表单,其中包含了一些简单的控件,包括Picker
、Toggle
、Stepper
、Button
。若是咱们把代码原封不动的放到 macOS 项目中,它会变成下面这个样子。
你会发现,一样的代码,展现出来的 UI 却彻底不同,但 UI 表达的内容确实彻底一致的。更使人细思极恐的是,Picker
这个"单项列表选择" 控件,在iOS上被翻译成了一个选择页面,点击 cell 会 push 出全部可选择的选项,点击选项会返回上一页面并将选中的内容展现到对应的 cell 上。而在 macOS 上,Picker
则会被翻译成咱们桌面操做系统上常见的下拉列表。这种转变是难以想象的,我只能用 Wow、Awesome、Amazing 来形容它。由于它彻底符合平台设计语言和用户使用习惯的,同时又极大的下降了开发和适配难度。然而 SwiftUI 能作的还远远不止这些。
没错,不只仅是 iOS 和 macOS,在 tvOS 和 watchOS 上,SwiftUI 也将以最符合平台设计语言和交互的方式去展现每个控件。并且,你还将自动得到大量的系统特性,好比 Dynamic Type、Dark Model、阅读方向自适应等。
从这一点上来看,SwiftUI 和 React Native 有着更为类似的思路和技术。只不过 Facebook 不管对 iOS 仍是 Android 都几乎没有任何话语权,所以在兼容性、一致性上都有必定问题。而为了达到这种兼容性,不只 React Native 自身须要作大量的工做,开发人员也须要疲于应对各类问题,能够说是一种比较尴尬的中间技术。
而 SwiftUI 和 React Native 与 Flutter 的跨 UI 平台思想有着本质的不一样。Flutter 彻底抛弃了运行平台的原生 UI 框架,相似于在一张画布上一个像素一个像素"画"出每个控件,相似于一个 2D 游戏引擎。它的目的也很明确:确保相同的代码在不一样的操做系统、硬件设备、屏幕尺寸下展现彻底相同的 UI 。这彷佛是开发者想要的,由于咱们受够了 RN 在不一样平台上表现出的不可控差别。但即使抛去了不一样操做系统设计语言的差别,不一样尺寸屏幕下的 UI 至少应该是很不同的。桌面平台 UI 和移动平台 UI,甚至手表上的 UI,应该根据用户的使用习惯和操控方式,制定彻底不一样的 UI 和交互。若是要在 Flutter 上实现多平台的适配,结果对任何一个程序员而言均可能是一场噩梦。你须要作大量的判断,而且可能须要使用多种控件来实现单一功能,最终还要面对大量的测试和 Bug。
不过有利就有弊。SwiftUI 近乎完美的跨平台方案是创建在较低自由度下的。经过上面的例子你也发现了,Demo 中使用的都是系统原生的控件样式。这并非说咱们没法自定义,只不过:
好比咱们想要修改列表背景的颜色,那么咱们将会失去部分系统自动为咱们添加的 Dark Model 特性,你须要作一些额外的工做来告诉 App 在黑/白模式下背景应该展现什么颜色。
但好在 Apple 已经开始尝试脱离系统原有的 UI 框架,这反而让咱们更加方便自定义部分 UI 。以往咱们要实现下面这个 Button 的样式,须要对 UIButton 的参数进行大量调整,由于默认的 UIButton 是文字在左,图片在右。现在,SwiftUI 脱离了系统 UI 框架组建后,咱们能够经过极其简单的代码实现复杂的 UI 样式。
不过这和 Flutter 引觉得傲的 "精确到像素" 还有必定差距,咱们可能没法像 Flutter 那样彻底掌控 UI 控件的全部细节。但就像画画同样,你失去了必定的自由,换来的是更快更高效的开发,这偏偏体现了 Apple 的企业风格。
然而 SwiftUI 跨平台的故事讲完了吗?我认为这偏偏只是个开始。SwiftUI 这个彻底与平台、设备无关的,纯描述的 UI 框架,偏偏才是跨平台方案的正确方向。SwiftUI 可以用来描述 iOS macOS tvOS 甚至 watchOS 的 UI,为何不能用来描述 Android,或者 Web?SwiftUI 已经拥有了 Flex 布局、combine 数据绑定、热加载等一系列特性,咱们只须要照着这个思路,将 Android 或者 Web 的模版套用在 SwiftUI 上,就能一样获得符合平台设计语言和规范的 UI 。固然,这远没有我说的那么简单,可是不要忘了,Swift 是开源的,跨平台运行也不是问题。只要条件存在,一切皆有实现的可能。
然而到那时,Flutter 又会变成什么样子呢?至少在目前,Flutter 仍是会成为大部分公司跨平台的首要方案之一,而 SwiftUI 嘛,大面积使用至少也是2~3年以后的事咯。