引言
在之前,我有写过关于 iOS 入门开发的一系列合集:iOS 入门系列合集,这其中既有关于 UIkit 的描述,也有关于 SwiftUI的描述;但主要是围绕 SwiftUI 展开的,对于 UIkit 只是轻描淡写
然而,上项目之后,用的框架又是 UIkit,因此 SwiftUI 的知识被渐渐遗忘,突然有一天想要整理 UIkit 的学习路线,便回顾了一下 iOS 入门系列合集 的内容,在看到关于 SwiftUI 的数据状态和数据流
部分时我有点懵了,一时间完全想不起来在 UIkit 中与之对应的实现是什么?因此本文便诞生了~
视图声明及布局
在 iOS 入门系列(二)这篇文章中有描述 UIkit 和 SwiftUI 在视图和布局上的不同的定义及实现方式
这里便再简要的总结一下:
- 在视图声明上的区别
- 使用的声明语法不同
- SwiftUI 的语法更为简洁和直观
- 视图属性设置不同:UIkit 需要通过属性或方法来设置,SwiftUI 直接通过链式调用的方式设置
- 在布局上的区别
- UIKit 需要使用容器视图(如
UIView
、UIStackView
)以及约束来实现复杂的布局 - SwiftUI 提供了
VStack
、HStack
、ZStack
等更语义化的容器视图,以及内置的修饰符(如padding
、spacing
等),使得布局的定义更加简单
- UIKit 需要使用容器视图(如
数据状态和数据流
在 iOS 入门系列(三) 一文中有对 SwiftUI 的数据状态和数据流
的知识点进行介绍:通过属性包装器
管理数据; 那么在 UIkit 中又是如何实现对于数据状态的管理的呢?
概念科普
UIkit 本身并没有提供专门的机制来直接管理应用程序的数据状态。UIkit 主要关注用户界面的创建、显示和交互,而数据状态通常由应用程序的数据模型 (Model) 或控制器 (Controller) 类来管理
UIkit 提供了一些工具,如:代理 (Delegate)、闭包 (Closure) 、通知中心 (NotificationCenter)、属性观察器等,用于在视图控制器、视图和模型之间传递数据和通知数据状态的变化,以达到同步更新视图的目的;同时,UIkit 也提供了关于视图重绘、重排的内置方法进行视图的更新
因此,在 UIkit 应用程序中,数据状态的管理是开发人员的责任,通常通过创建和维护数据模型或控制器类来实现。这些类会包含应用程序的数据逻辑,包括数据状态的管理和更新。代理、闭包和通知中心等工具可用于协助数据状态的传递和同步更新,但它们并不直接管理数据状态
可视化解析
上图分别画出了在 SwiftUI 和 UIkit 中如何对视图进行更新的系列操作,可以很明显看出 SwiftUI 采用属性包装器
直接可以省略掉VC
这一中间层,使视图的更新更简单直接;在 UIkit 中要实现视图与数据同步更新则需要借助VC
这一中间层并配合代理
、通知中心
、闭包
和属性观察器
来实现
拓展
代理、通知中心概念
关于代理
可以参考 iOS 近期开发概念小结一文的delegate 就是一个属性? 部分的内容配合理解消化;
通知中心的使用相对简单,因为是全局的通知,所以只需要在发送通知的地方定义通知,然后在需要接收通知的地方注册并处理它即可,下面给出案例参考:
发送通知
swift
import UIKit
// 定义通知名称
let myNotificationName = Notification.Name("MyCustomNotification")
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// 发送通知
NotificationCenter.default.post(name: myNotificationName, object: nil)
}
}
接收通知
swift
import UIKit
class AnotherViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// 注册通知
NotificationCenter.default.addObserver(self, selector: #selector(handleCustomNotification), name: myNotificationName, object: nil)
}
// 处理通知的方法
@objc func handleCustomNotification() {
print("Received custom notification")
}
deinit {
// 当不再需要接收通知时,取消注册通知
NotificationCenter.default.removeObserver(self, name: myNotificationName, object: nil)
}
}
UIkit 视图重排、重绘
在上面已经提到过 UIkit 关于视图的重绘和重排的内置方法,这里就简略的小结一下它们的一些区别:
重绘和重排概念
- 重绘:修改视图上的文本、图片等可见内容
- 调用
draw(_:)
方法,可自定义
- 调用
- 重排:修改视图的位置、尺寸和布局属性
- 调用
layoutSubviews
方法,可自定义
- 调用
- 简短总结:重绘性能消耗高于重排
UIkit 中重排的2种方法
- setNeedsLayout():异步更新请求,不会立即触发布局,而是在当前运行循环结束后,系统会检查需要更新布局的视图,并在合适的时机执行
layoutSubviews
方法 - layoutIfNeeded():用于强制立即执行布局的,不必等待系统的下一次布局更新
延伸概念
- 系统会有多个事件循环,每个事件循环都处理不同类型的事件。UIKit 应用程序通常包括主 Run Loop 和任意数量的自定义 Run Loops
- 主 Run Loop 是应用程序的主要事件循环,负责处理用户交互事件(如触摸事件、按钮点击等)以及其他系统事件
- 自定义 Run Loops可以用于其他目的,例如:在后台执行任务
- 每个 Run Loop 都有自己的循环周期,而且会不断运行,等待和处理事件。这有助于应用程序响应用户交互、网络请求、计时器事件等
setNeedsLayout
方法通常用于告知系统在当前 Run Loop 结束时或下一个 Run Loop 中进行布局更新,以充分利用系统的事件循环来降低性能开销
小结
在 SwiftUI 中,通过属性包装器
创建具有数据绑定的视图模型,在数据更改时,视图会自动更新以反映这些更改。这样的方法使得在 SwiftUI 中管理数据状态和更新视图更加直观和简化
在 UIkit 中,没有专门的机制来直接管理应用程序的数据状态。因此就需要借助Viewcontroller 层
配合着代理
、通知中心
、闭包
、属性观察器
来实现数据与视图的同步更新