SwiftUI入门

tech2025-03-16  8

摘要

这篇分享的内容是关于SwiftUI的一些知识,主要分为以下几个方面:SwiftUI的概念和用法;WWDC 20大会上对swiftUI的更新介绍;SwiftUI在开发应用中的优缺点分析以及它和我们目前开发中使用的UIKit的比较;SwiftUI的发展前景等。


前景

从以下数据可以看出,未来swift将是iOS开发的唯一选择,越早进行相关的迁移工作会对未来公司的发展越有利:

从 WWDC17 后 苹果已经不再使用 Objective-C 做 Sample Code 演示https://developer.apple.com/不再更新Objective-C 相关的文档WidgetKit 是 SwiftUI onlyApp Clips 10M的包大小, SwiftUI 是最合适的框架开源社区逐步放弃 Objecive-C 如 Lottie

SwiftUI介绍

SwiftUI是Apple在WWDC19上提出来的一个基于Swift编程语言,Xcode编译器的声明式的布局引擎,具有良好的跨平台特性,可以为所有Apple平台上的应用编写界面,并且支持同步预览,可视化编辑等功能。

基础概念

swiftUI控件

Text:展示静态文本内容的控件

Image:展示图片的控件

HStack,VStack,Zstack:用来进行控件的不同排列,包括水平,垂直和subviews

struct ContentView: View { var body: some View { VStack { Image("chilkoottrail") .frame(width: 300, height: 300, alignment: .center) .clipShape(Circle()) .overlay( Circle().stroke(Color.white, lineWidth: 4)) .shadow(radius: 10) Text("chilkoottrail") .font(.title) } } }

List:list是swiftUI中的一个列表容器,实现的效果类似于UITableView,为我们提供一个可以滚动显示数据的表格。

NavigationView:为我们的界面顶部提供一个导航视图,被navigationView包裹起来的组件都会处于一个视图栈中,可以进行页面的push和pop切换,一般会有三个控件和navigationView结合使用:

navigationLink:可以将一个新的视图推进视图栈中,跳转的目标界面需要用到navigationLink来设置。功能类似于pushViewController struct ContentView: View { var body: some View { NavigationView { NavigationLink(destination: Detail()) { Text("haha") } .navigationBarTitle("ded", displayMode: .inline) .navigationBarItems(leading: Button("lead"){}) } } }

我们只需要给NavigationLink提供跳转的目标界面和需要点击的控件,其他的NavigationLink会自己完成,虽然Text是一个静态的文本控件,但是这里NavigationLink给他提供了点击的能力。

navigationBarItems:用来设置导航栏的左右按钮navigationBarTitle:导航栏的标题及样 Inline:展示标准导航栏的title样式large:系统默认的样式,居左大标题automatic:继承前一个页面的导航栏样式

为什么navigationBarTitle是被添加在附属视图上面,而不是直接添加在navigationView上?

每个页面都需要一个title,这里设置的是当前页面的title而不是整个navigationView的title,一个navigationView中可能会有很多个页面,当其处于navigationView中时这个title就会被展示出来,但是navigationView本身并没有处于一个导航视图中,没有视图栈,所以无法显示。

Modifier

在SwiftUI中,对视图属性的设置被称为视图的修饰器(Modifier),通常来说Modifier可以修改的内容包括以下几个方面:

布局:通过调整视图的大小、位置、对齐、填充等来告诉视图如何在视图层次结构中布局自己。渲染:影响系统绘制视图,例如对视图进行缩放、遮罩,以及应用图形效果等等。样式:指示如何样式化视图中包含的文本、控件和其他内容,多用于复杂视图,如选择器等等。其他:前三个基本属于配置视图的范围,剩下还包含一些添加手势、呈现弹窗、或是事件处理等等杂七杂八但也是很重要的一部分。 struct ContentView: View { var body: some View { Text("Hello World") .font(.largeTitle) .foregroundColor(.white) .padding() .background(Color.blue) .clipShape(RoundedRectangle(cornerRadius: 10)) } }

除了系统内置的Modifier之外,我们也可以使用ViewModifier协议把一系列的Modifier封装起来,来实现我们自定义的Modifier,起到简化代码,代码复用,增加可读性的作用:

struct Title: ViewModifier { func body(content: Content) -> some View { content .font(.largeTitle) .foregroundColor(.white) .padding() .background(Color.blue) .clipShape(RoundedRectangle(cornerRadius: 10)) } } struct ContentView: View { var body: some View { Text("Hello World") .modifier(Title()) } }

声明式布局

例子:指定两个朋友帮你通关游戏

朋友A:你只需要告诉他目标,把这个游戏玩通关,他会自己去摸索如何打怪升级,如何买装备等,不用你再手把手教他怎么玩。朋友B:不关心目标,你说什么他做什么,说一步做一步,需要你一步步指导他去玩,直到通关。

声明式布局体现了一种Coder do less, Framework do more的工作思想。它描述目标的性质,让计算机明白目标,而非流程,底层的软件/框架去解决如何实现他们;与之对立的是命令式布局,我们告诉计算机的是一个个指令,先做什么,再做什么,最后做什么,计算机不理解我们的目标,而只是按照我们的命令一步步去执行。

比如我们要在屏幕上放一张图片,两种方式的不同表现形式如下:

声明式布局 只告诉系统屏幕上的什么位置有一张什么图片,并不关心具体如何实现,这是框架要做的事情。 //声明式布局 struct ContentView: View { var body: some View { Image("chilkoottrail") } } 命令式布局 初始化一个UIImageView设置frame添加到视图树中 //命令式布局 - (UIImageView *)imageView { if (!imageView) { _imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"chilkoottrail"]]; _imageView.frame = CGRectMake(12, 12, 100, 100); } return _imageView; }

同步预览

使用Xcode中的canvas,可以让我们在使用SwiftUI编写界面的同时观察到实现的效果,代码中的视图会立即以预览形式显示,Canvas 常规的显示规则是,总是显示当前编辑器里可预览的文件,使用左下角的 Pin Preview,可以在切换不同文件的时保持显示被 pin 时候的 Preview 界面。

Inspector

使用Canvas不仅可以预览界面的布局效果,也还可以在预览界面中对视图的属性进行修改,代码中会马上出现对应的内容。通过在canvas里双击选中元素,在右侧的Inspector里进行修改,inspector是一个可视化的编辑器,可以在其中进行某个控件属性的修改,或者添加新的Modifier。

默认会显示的内容有下面四种:

视图类型PaddingFrameAdd Modifier

Add Modifier 中包含最丰富的内容,当不知道当前选中的对象有哪些方法时,Add Modifier 的弹窗是个非常好的查询入口。Xcode 自动把 Modifier 分为:

Control (多为可响应用户事件的元素)Layout(布局、背景、大小尺寸信息)Effect(颜色对比、渐变、形变等)Event (显示隐藏、通知到达等)Style(系统组件的默认样式配置)......

可视化修改、生成视图

SwiftUI代码中我们可以通过按住Command键,点击对应的控件,来修改或者添加新的视图,也可以通过该方法添加一些视图容器来修改视图的布局。

目前支持的一些actions:

包裹在 HStack 容器里包裹在 VStack 容器里包裹在 List 容器里包裹在 Group (非容器)里引入条件使循环 n 次(默认 5 次)呼出本视图元素的 Inspector(这样可以隐藏最右侧的 attribute inspector 最大化屏幕利用率)

平台支持

SwiftUI良好的跨平台特性指的是它编写的一套界面代码可以同时运行在iOS,macOS以及tvOS等苹果公司的各系统中,完全无需引入任何 UIKit APPKit WatckKit 等相关Framewok。

它将整个原有的平台差异部分抽象为 App 和 Scene,对于一个 mac/iOS/iPad/watch/tv应用来说, App 代表了整个应用,Scene 代表了与 Window 相关的多窗口,有些设备只有一个 Scene 有些则有多个,虽然不同的 OS 确实存在差异,但是在语义层面达到了一致。

但是苹果也特意指出,没有一种方法能万能的适配到所有设备上。如果有势必会抹掉很多该平台特有的体验。这虽然对开发者友好,但可能会降低用户的体验。

针对多设备的设计理念:

针对对应用的平台选择对应的合适的设计认真分析共享模块的设计共享视图是个有风险的行为,要考虑的更加深入Learn once apply anyware

SwiftUI的布局算法

SwiftUI会通过body的返回值获取描述视图的控件信息,转换为对应的内部视图信息,交给2D绘图引擎Metal或者Open GL绘制。

ContentView import SwiftUI struct ContentView : View { var body: some View { Text("Hello World!") } }

上面这段代码的视图层级是RootView -> ContentView -> Text,关于Text出现在屏幕上的官方描述是:

父视图为子视图提供预估尺寸(建议尺寸)子视图计算自己的实际尺寸(一般有三种方式) 无需计算,根据内容推断,如 Image 是和图片等大,Text 是计算出来的可视范围,类似 NSString 根据字体计算宽高。Frame 强制指定宽高设置缩放比例 如 Image 设置 aspectRatio父视图根据子视图的尺寸将子视图放在自身的坐标系中,默认居中 使用Modifier的布局结构: import SwiftUI struct ContentView : View { var body: some View { Text("Hello World!") .padding(10) .background(Color.Green) } } struct Avocado : View { var body: some View { Image("20x20_avocado") .frame(width: 30, height: 30) } }


WWDC 20

跨平台

swiftUI2.0在创建新工程时,会生成一套全新的模板基于SwiftUI App Lifecycle 的跨平台项目。下面是新旧代码的对比。

Before: class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { let contentView = ContentView() if let windowScene = scene as? UIWindowScene { let window = UIWindow(windowScene: windowScene) window.rootViewController = UIHostingController(rootView: contentView) self.window = window window.makeKeyAndVisible() } } } After: import SwiftUI @main struct MyApp: App { var body: some Scene { WindowGroup { ContentView() } } }

Xcode Library

对于一个大型的开发团队而言,一个开发同学是很难知道公司内到底有多少种组件库,而且即便知道有某种组件库,开发同学初期看到的也是代码,一般需要书写一定的 Demo 才可以用眼睛感知到这个组件到底是否是我想要的。

在 Xcode 12 中提供了更强大的工具,一个自定义组件,只需要遵守一个 LiberyContentProvider 协议就可被Xcode识别,可以像系统控件一样直接从 Xcode 里面识别并预览。对于一个大型团队来说,此功能可以大大提高找寻组件和查看组件样式的效率。

大列表组件

对于传统的命令式编程来说,我们可以主动控制 UITableViewCell 的重用,自建缓冲池等一系列手段去优化我们的 APP 内存占用,但是对于 SwiftUI 1.0 来说,系统提供的控件并没有有效的办法去让我们控制页面的渲染,对于大列表页面就容易出现内存占用过高问题。SwiftUI 2.0 推出了 LazyHStack 和 lazyVStack 加上 List 渲染模式默认就是 Lazy 的直接解决了最大的性能问题。


SwiftUI vs UIKit

安全性:SwiftUI基于Swift语言,Swift的引入会让代码尽可能的减少未定义的行为,减少crash。APP的稳定性就会得到提高,用户会获得更好的使用体验。包大小:如果一个APP的UI部分全部使用SwiftUI来编写,这种声明式的编程语言代码量会小于传统的命令式语言,所以整个APP的包大小也会降低,或者说相同的包大小就可以承载更多的业务。开发体验和效率: UI的调整可以及时得到反馈,这对于微小的UI微调工作是一个极大的帮助,节省时间。支持的可视化编辑工具也为开发人员的工作带来了很大的便利。Coder do less, Framework do more。 内存:Swift语言使用值类型构建app,减少了指针的使用,减少了大量堆内存的消耗,降低了APP的整体内存消耗。局限性 只支持iOS13及以上的系统,而目前市场上很多APP还在支持iOS9。不够成熟,从去年WWDC19被提出来到现在只经过了一年多的发展,还没有经历过大型APP的考验。

参考文档

https://www.jianshu.com/p/f728dd085593

https://www.jianshu.com/p/8d8c9702ff0a

https://mp.weixin.qq.com/s/4TwguxVfjqHz-YLen-vh7A

https://mp.weixin.qq.com/s?__biz=MzAxNDEwNjk5OQ==&mid=2650408836&idx=1&sn=e1cd3c705ba49aa8613b3adc21e14cd6&scene=21#wechat_redirect

https://mp.weixin.qq.com/s?__biz=MzAxNDEwNjk5OQ==&mid=2650402891&idx=1&sn=804f271d6794a0ec7b5eeaea585f5e8e&scene=21#wechat_redirect

https://mp.weixin.qq.com/s?__biz=MzI2NTAxMzg2MA==&mid=2247483748&idx=1&sn=ca06c93e207009d40e17bf2f95d76eb6&scene=21#wechat_redirect

https://developer.apple.com/cn/xcode/swiftui/

https://developer.apple.com/tutorials/swiftui/creating-and-combining-views

最新回复(0)