SwiftUI入门
与大家一起学习一下SwiftUI
历史背景回顾
- WWDC2014 推出Swift,现最新版本 Swift 5
- WWDC2016 苹果公司为iPad推出Swift Playgrounds App,旨在让学生和初学者更容易地掌握 Swift 学习编程的技能。
- WWDC2019于 今年6月份举行,发布了SwiftUI
SwiftUI介绍
- 实时模式 及 新的直观的设计工具(Preview 需要xcode11、macOS 10.15)
- 设计画布中的操作与编辑器中代码保持完全同步
- Swift 编译器和运行时已全面嵌入到 Xcode 中,您可以随时构建和运行您的 app
- 声明式语法
- 原生支持所有Apple平台(iOS、OSX、iPadOS、tvOS、watchOS…)
为什么推出SwiftUI呢?
- 为苹果各生态提供统一的UI框架,降低其平台间开发门槛(假如去开发macOS,不需要再学一次 AppKit)
- 大幅提高UI开发效率
- 声明式语法,结合新推出的响应式框架(Combine),iOS生态下终于可以像Flutter、React 那样写代码
2014-2019年,开发者可以无视Swift,继续用Objective-C,但是2019 年开始,一切都不一样了
声明式编程的几个优点:
- 代码是描述程序做什么,阅读性较高
- 组件状态更容易维护在内部,减少副作用
- 组件间边界更明显,从而让组件复用更灵活
近年来,函数式或申明式开发UI方式逐渐成为主流,如Flutter、React,这些开发框架一般的开发步骤如下:
- 使用各自的DSL来描述【UI应该是什么样子】,而不是用代码命令式的执行【要怎么构建UI】
- 框架内部读取这些view的申明,负责将其以合适的方式绘制渲染
- 如果view需要根据某个状态(state)来改变,则将状态存储在变量中,并在声明view时使用它
- 状态改变时,框架重新调用声明部分的代码,计算出新的view申明,并和原来的view进行diff,之后由框架负责将变更的部分进行高效重绘
SwiftUI文档
| SDKS | 适用版本 | | - | - | | SDKs | iOS13.0+ | macOS10.15+ | Mac Catalyst13.0+ | tvOS13.0+ | watchOS 6.0+
因为SwiftUI需要的iOS系统支持最低13.0,所以在实际项目中运用的话,可能需要等一两年
申明每个平台上的你的应用的用户界面和行为
概览
SwiftUI提供了用于声明应用程序用户界面的视图(views)、控件(controls)、布局结构。该框架为分发点击、手势和其他类型的输入提供事件处理程序,并提供工具来管理从应用的models到用户将看到并与之交互的views和controls间的数据流。
创建遵守View协议的自定义视图,然后将它们与SwiftUI的视图组合在一起,以使用stacks, lists等来显示文本、图片、自定义shapes。将强大的修改器应用于内置视图和自定义视图,以自定义其渲染和交互性。多个平台的应用之间可以共享代码,这些视图和控件可以适应多个平台的上下文和表现形式。
可以将SwiftUI视图与UIKit,AppKit,WatchKit框架中对象集成在一起,以进一步利用特定平台的功能。SwiftUI支持自定义辅助功能(accessibility),并针对不同的语言,国家、文化区域做本地化用户界面。
###话题 ***
要点
学习使用SwiftUI制作应用 遵循一些列指导教程,学习使用SwiftUI和Xcode制作应用程序 ***
用户界面
| 视图与控件 | 在屏幕上显示内容并处理用户交互|
视图和控件是应用中用户界面的视觉组成部分。视图包括text、images、shapes、自定义绘图及所有的这些组合组成。控件是用户能够与适应其平台和上下文一致的api进行交互。
| 视图布局与表现 | 将视图组合在栈中,动态生成视图组和列表,并定义视图表现和层次结构 |
使用stacks和lists来布局用户界面的视图。可以将视图与从数据集合中动态生成的视图集合起来。所有容器视图都会根据内容或界面尺寸来更新和调整其子集的位置
| 绘画和动画 | 使用颜色、形状、阴影增强视图,并自定义视图状态之间的动画过度 |
| 框架整合 | 将SwiftUI视图集成到现有的应用程序中,并将AppKit、UIKit、WatchKit视图和控制器嵌入到SwiftUI视图层次结构中 |
数据与事件
| 状态与数据流 | 控制和响应应用程序模型中的数据流和编程 |
States 和 bindings将视图连接到应用的基础数据模型。当声明一个state,SwiftUI会为你存储该状态并管理该状态与视图的连接。当状态更新时,视图将使其外观无效并自动更新。还可以将动画连接到状态,以视图的动画形式来描述状态的变更。
从状态的成员创建绑定以连接到各个视图。绑定提供双向连接,所以屏幕上的控件可以改变状态。绑定也具有在视图间传递值的事务。
| 手势 | 定义从点击、点击、滑动到细粒度手势的交互 | ***
Xcode中的预览
| 预览 | 生成自定义视图的动态交互预览 | ***
学习使用SwiftUI制作应用
- 初始化工程里的小知识点
- swift工程里是没有main.m文件的,程序启动入口 可以理解为由 @UIApplicationMain自动生成需要的main函数
- xcode11新建工程会多一个SceneDelegate,是为了支持iPadOS多窗口的原因,如果不需要支持多窗口,可以手动删除info.plist文件中的Application Scene Manifest的配置数据、Scene等相关代码。纯代码实现时,需要手动在AppDelegate里添加window属性
- UIHostingController是UIViewController的子类,负责接收一个SwiftUI的描述并将其用UIKit进行渲染
- associatedtype关键字声明一个类型的占位符作为协议定义的一部分,协议中实现范性功能的语法;class,struct,enums都可以使用参数化类型来表达泛型的
- SwiftUI知识点
***
- 自定义SwiftUI视图时,可以调用称为modifiers的方法。Modifiers包装视图以更改其显示或其他属性。每个modifier都会返回一个新的视图,因此可以进行链式调用
- 创建SwiftUI视图时,可以在body属性中描述其内容、布局和行为。但body属性只能返回单个视图,多个视图的组合可以使用Stacks。(HStack 子view排列在一条水平线上的视图 VStack 垂直排列 ZStack 从后向前)
- 在swiftUI文件中需要引用UIKit时,需要遵守UIViewRepresentable协议。其协议需要实现两个方法:
1.创建view的方法 func makeUIView(context: Self.Context) -> Self.UIViewType
2.配置视图并响应所有更改的方法func updateUIView(_ uiView: Self.UIViewType, context: Self.Context)
- 当”预览”是在静态模式时,仅仅渲染SwiftUI的视图。当视图时UIView的子类时,需要把”预览”切换到实时预览才能看到效果 ***
- Image方法resizable()可以根据不同模式缩放图片
- preview可以通过修改视图的初始化参数直接更新预览,可以通过previewLayout方法修改合适的预览尺寸,比如设置为列表中一行的大小;可以通过Group容器添加多个预览,Xcode将卒中的子视图呈现为画布中的单独预览;
- 视图的子集继承视图的上下文设置
-
列表的使用需要identifiable的数据;使数据identifiable由两种方式,
- 通过将数据传递给唯一标示每个元素的属性的键路径
- 使数据类型符合 identifiable协议
-
常规的属性传值
-
默认预览设备类型由scheme中选择的模拟器类型决定的;PreviewDevice初始化参数字符串可以在view的扩展的注释里找到所有支持的设备名称,搜一下”iPhone SE”;预览中多个设备同时显示,对比界面视图时比较方便,不过实时连接的设备只能有一个,类似于模拟器可以开很多,但是调试时只有一个是连接上的;
- 遗留问题点
- navigationBarTitle 设置 导航条标题 是view的方法,而不是NavigationView的方法,试了一个NavigationView的子集由两个view都设置 导航条方法时,只显示了第一次设置的,待研究
- NavigationLink的定义为A button that triggers a navigation presentation when pressed. 不知道有没有 不跳转的方法,只是单纯的点击事件
- 例子中的cell样式是系统的,不知道怎么自定义,比如右边的箭头去掉,还有就是行高怎么控制
- if 语句可以根据条件来包含视图
- 状态是一个值或一组值,可随时间变化,并影响视图的行为、内容、布局(@State 属性)
- 要将静态视图和动态视图合并到列表中,或者将两个或多个不同的动态视图组合并,需要使用ForEach类型,而不要将数据集合传递给List
- 使用$前缀来访问对状态变量或状态变量的某个属性的绑定;绑定充当对可变状态的引用;控件可以使用绑定来相应的更新视图的状态
- observable对象是一个自定义数据对象,可以将其绑定到SwiftUI环境中的存储视图;SwiftUI监听可能影响视图的observable对象的任何变更,并在更改后显示视图的正确版本;
- SwiftUI订阅一个自定义的observable对象,并在数据更改时更新需要刷新的所有视图。ObservableObject协议需要导入框架Combine;
- 为了让一个observable对象的订阅者可以获取更改,需要对象发布其对数据的变更(在需要的数据属性前加上 @Published)
不同场景中,SwiftUI 提供了不同的关键词,其实现原理上如上文所示:
@State - 视图和数据存在依赖,数据变化要同步到视图;
@Binding - 父子视图直接有数据的依赖,数据变化要同步到父子视图;
@BindableObject - 外部数据结构与 SwiftUI 建立数据存在依赖;
@EnvironmentObject - 跨组件快速访问全局数据源
应用程序设计和布局
- Dictionary(grouping: , by:)方法将数组转成Dictionary
- listRowInsets(EdgeInsets()) 扩展内容到显示的边缘
- 传给NavigationLink的Text默认使用环境的强调色呈现,Image会呈现为模版图像,需要修改合适的设计。
使用UI控件
-
.frame(width: 300, height: 300).scaleEffect(1.0 / 3.0) .frame(width: 100, height: 100) 相当于view的等比例缩放
-
.hueRotation(Angle(degrees:90)) 对view进行hue 颜色旋转效果
-
Divider() 分割线
与UIKit的框架整合
- 为了在SwiftUI中表示UIKit中的view和viewController,需要创建遵守UIViewRepresentable和UIViewControllerRepresentable协议的类型。当创建自定义类型并配置相应的UIKit类型的表现形式,而SwiftUI管理其生命周期并在需要时更新更新。
- 当装备显示视图时,SwiftUI会调用一次makeUIViewController方法,然后管理视图控制器的生命周期
- 表示UIKit中控制器的那个SwiftUI视图可以定义为Coordinator类型,次类型由SwiftUI管理并作为表示视图上下文的一部分提供。
- SwiftUI管理UIViewControllerRepresentable类型的协调器,并在调用上面创建的方法时将其作为上下文的一部分提供。
- SwiftUI在调用makeUIViewController之前调用makeCoordinator方法,以便在配置视图控制时可以访问协调器
- 可以使用协调器来实现常见的Cocoa模式,比如代理、数据源、通过target-action响应用户事件
开预览 可能对机器性能要求较高,学习官网教程过程中,查看api时电脑卡死2次,切到其他程序时卡死1次。
引用源