标识符(Identifier)范围扩展与反引号
详细介绍
Swift 6.2 显著扩展了标识符可用字符范围;配合反引号可以使用空格、数字开头或与关键字冲突的名称作为标识符。
典型用途:桥接外部接口命名、演示代码、避免与关键字冲突、为 DSL 提高可读性。
注意:建议仅在必要时使用,保证团队可读性与一致性。
示例代码
swift
复制代码
// 反引号可用于包含空格/数字/关键字等非常规标识符
struct Person {
var `full name`: String
var `class`: String // 关键字作为属性名
}
func `run task`(_ value: Int) -> Int { value * 2 }
let 🧭 = "north" // 扩展的可用字符示例(Emoji)
let p = Person(`full name`: "Ada Lovelace", `class`: "VIP")
let output = `run task`(21)
print(p.`full name`, p.`class`, 🧭, output)
字符串插值支持默认值
详细介绍
可选值插入字符串时,可在插值处直接提供默认值:当可选为 nil 时自动使用默认值。
相比以往使用 ??
的写法,插值默认值语义更直观、噪音更少。
示例代码
swift
复制代码
let nickname: String? = nil
let age: Int? = nil
print("Hi, \(nickname, default: \"Guest\")") // Guest
print("Age: \(age, default: 18)") // 18
let payload: [String: String]? = ["city": "Hangzhou"]
print("City: \(payload?["city"], default: \"Unknown\")")
InlineArray(定长数组)
详细介绍
新增定长数组类型 InlineArray<N, Element>
,在栈上紧凑存储,具备更好的性能与缓存局部性,适合小容量、频繁创建/销毁的场景。
可使用类型推断省略容量参数(当字面量可确定大小时)。
示例代码
swift
复制代码
var numbers: InlineArray<4, Int> = [1, 2, 3, 4]
var letters: InlineArray = ["A", "B", "C"]
var sum = 0
for n in numbers { sum += n }
print("sum =", sum) // 10
for (i, ch) in letters.enumerated() {
print(i, ch)
}
enumerated() 返回类型遵守 Collection
详细介绍
enumerated()
的返回类型在 6.2 起遵守 Collection
协议,可直接用于需要集合语义的 API(如 SwiftUI List
)。
优势:无需 Array(...)
包装,减少不必要的分配与拷贝。
示例代码
swift
复制代码
let names = ["ZhangSan", "LiSi", "WangWu", "ZhaoLiu"]
// 直接在 enumerated() 上链式使用 Collection 能力(无需 Array(...) 包装)
let evenIndexed = names
.enumerated()
.filter { $0.offset % 2 == 0 }
.map(\.element)
print(evenIndexed) // ["ZhangSan", "WangWu"]
并发编程语义调整与 @concurrent
详细介绍
行为变化:6.2 之前,nonisolated
异步函数会在后台线程执行;6.2 起默认在调用者的 actor 上执行。
新增 @concurrent
:
让函数在后台线程运行(即使从主线程调用)。
创建与调用者分离的新隔离域。
所有参数与返回值必须符合 Sendable
。
适用:耗时/CPU 密集或潜在阻塞型任务(大量数据转换、I/O 操作等)。
示例代码
swift
复制代码
actor SomeActor {
// CPU 密集型任务:后台并发执行,参数/返回须 Sendable
@concurrent
nonisolated func heavyCompute(_ input: [Int]) async -> Int {
input.reduce(0, +)
}
}
@MainActor
func demo() async {
let data = Array(0...1_000_00) // 10 万项
let result = await SomeActor().heavyCompute(data)
print("result =", result)
}
UIScene 打开外部文件
详细介绍
场景 : App 内存在不受自身支持的文件类型,需要委托系统或其他 App 打开。
能力点 : 使用 UIWindowScene.open(_:options:completionHandler:)
打开位于沙盒可访问位置的文件 URL。
要点 :
将 Bundle 文件拷贝至可写目录(如 tmp/
)再打开,避免只读路径限制。
首选前台激活的 UIWindowScene
;无前台场景时给出用户可理解的降级提示。
处理回调 success
与错误路径,必要时提示"缺少可处理此类型的 App"。
示例代码
swift
复制代码
import UIKit
final class OpenFileViewController: UIViewController {
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let src = Bundle.main.url(forResource: "sample", withExtension: "zip") else { return }
openExternally(fileURL: src)
}
@MainActor
private func openExternally(fileURL: URL) {
let dst = URL.temporaryDirectory.appendingPathComponent(fileURL.lastPathComponent)
try? FileManager.default.removeItem(at: dst)
do {
try FileManager.default.copyItem(at: fileURL, to: dst)
} catch {
print("copy failed: \(error)")
return
}
guard let scene = UIApplication.shared.connectedScenes
.compactMap({ $0 as? UIWindowScene })
.first(where: { $0.activationState == .foregroundActive }) else {
print("no active scene")
return
}
scene.open(dst, options: nil) { success in
print(success ? "已交由系统/他端 App 打开" : "打开失败或无可用 App")
}
}
}
UIColor HDR 曝光(Exposure/Linear Exposure)
详细介绍
场景 : 在支持 EDR/HDR 的设备上呈现高亮度色彩与过曝细节。
能力点 : UIColor
新增 exposure
与 linearExposure
构造,UIColorWell
、UIColorPickerViewController
支持 HDR 选择。
要点 :
HDR 显示依赖硬件与系统显示设置,SDR 屏幕回退为常规显示。
可以设置 maximumLinearExposure
限定取色上限;吸管可通过 supportsEyedropper
控制。
示例代码
swift
复制代码
import UIKit
final class HDRColorViewController: UIViewController {
private lazy var colorWell: UIColorWell = {
let well = UIColorWell()
well.title = "HDR 背景"
well.maximumLinearExposure = 2.0
well.supportsEyedropper = false
well.addTarget(self, action: #selector(onColor), for: .valueChanged)
return well
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor(red: 1, green: 0, blue: 0, alpha: 1, exposure: 2.5)
view.addSubview(colorWell)
colorWell.center = view.center
colorWell.sizeToFit()
}
@objc private func onColor() {
view.backgroundColor = colorWell.selectedColor
}
}
UISlider 刻度与样式(TrackConfiguration/Style)
详细介绍
场景 : 需要"离散刻度"与"无拇指轨迹"选择器体验(音量档位、配置项选择)。
能力点 : sliderStyle
与 trackConfiguration
支持内置刻度与自定义刻度,并可仅允许落在刻度上。
要点 :
numberOfTicks
快速生成均分刻度;或提供 ticks
自定义不均匀刻度。
allowsTickValuesOnly=true
时配合手动"吸附"提升易用性。
neutralValue
可以设定一个基准点,让进度条可以分成左右两个进度,类似音效均衡器中的默认值,或参数的零点
enabledRange
定义滑块的有效范围,范围之外不可以交互
示例代码
swift
复制代码
import UIKit
final class TickedSliderViewController: UIViewController {
private lazy var slider: UISlider = {
let s = UISlider()
s.sliderStyle = .default
var cfg = UISlider.trackConfiguration = UISlider.TrackConfiguration(
allowsTickValuesOnly: true,
neutralValue: 0.5,
enabledRange: 0...1,
numberOfTicks: 11 // 0.0 ~ 1.0, 11 个刻度
)
cfg.allowsTickValuesOnly = true
s.trackConfiguration = cfg
s.addTarget(self, action: #selector(onChange), for: .valueChanged)
return s
}()
override func viewDidLoad() {
super.viewDidLoad()
slider.frame = CGRect(x: 20, y: view.center.y, width: view.bounds.width - 40, height: 44)
view.addSubview(slider)
}
@objc private func onChange(_ sender: UISlider) {
print(slider.value)
// 吸附到最近刻度,测试enabledRange时注释下面代码
let ticks: Float = 10
sender.value = round(sender.value * ticks) / ticks
}
}
详细介绍
场景 : 以导航栏按钮为触发源的 zoom 转场,突出从按钮"放大"至目标页的空间感。
要点 :
设置 preferredTransition = .zoom { ... }
并返回触发的 UIBarButtonItem
。
保证回调中的 zoomedViewController
类型正确。
示例代码
swift
复制代码
import UIKit
final class ZoomSourceVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.rightBarButtonItem = UIBarButtonItem(systemItem: .add, primaryAction: UIAction { [weak self] _ in
let next = UIViewController()
next.view.backgroundColor = .systemPink
next.preferredTransition = .zoom { context in
guard context.zoomedViewController === next else { return nil }
return self?.navigationItem.rightBarButtonItem
}
self?.present(next, animated: true)
})
}
}
详细介绍
要点 :
UIButton的Configuration新增glass、clearGlass、prominentGlass、prominentClearGlass方法,实现 Liquid Glass 风格。
新增symbolContentTransition 实现 SF Symbols 的带动画替换。
能力点 :
UIButton.Configuration.glass()
UIButton.Configuration.clearGlass()
UIButton.Configuration.prominentGlass()
UIButton.Configuration.prominentClearGlass()
symbolContentTransition
示例代码
swift
复制代码
import UIKit
final class GlassButtonViewController: UIViewController {
private let button = UIButton(type: .system)
override func viewDidLoad() {
super.viewDidLoad()
var cfg = UIButton.Configuration.prominentGlass()
cfg.title = "点赞"
cfg.image = UIImage(systemName: "hand.thumbsup")
cfg.preferredSymbolConfigurationForImage = .init(pointSize: 20, weight: .regular)
cfg.symbolContentTransition = UISymbolContentTransition(.replace, options: .speed(0.12))
button.configuration = cfg
button.isSymbolAnimationEnabled = true
button.addAction(UIAction { [weak self] _ in self?.toggle() }, for: .primaryActionTriggered)
button.frame = CGRect(x: 100, y: 200, width: 180, height: 56)
view.addSubview(button)
}
private func toggle() {
let filled = (button.configuration?.image == UIImage(systemName: "hand.thumbsup.fill"))
button.configuration?.image = UIImage(systemName: filled ? "hand.thumbsup" : "hand.thumbsup.fill")
}
}
UIVisualEffectView 的 UIGlassEffect 与 Container
详细介绍
场景 : - UIGlassEffect 是 iOS 26 引入的 UIVisualEffect 的子类,用来呈现系统级的 "Liquid Glass(液态玻璃)"材质 :半透明、折射、高光、随环境和尺寸自适应的玻璃视觉效果。它通常与 UIVisualEffectView 配合使用。
能力点 :
外观样式 :支持至少 .regular 与 .clear 等玻璃样式,视觉随暗/亮模式与背景内容自动适配(更大尺寸更不透明、更小尺寸更清晰)
可交互性(isInteractive) :设置为 true 后,系统会为玻璃上的交互元素提供内建触感反馈、缩放 / 弹跳等交互行为,使自定义控件与系统控件在交互感受上一致
着色(tintColor) :可为玻璃设置 tintColor,系统会自动生成"vibrant"版本供玻璃上的内容使用,便于做高亮或品牌色
角与形状(cornerConfiguration) :Glass 默认是胶囊(capsule)形状;WWDC 中演示了 cornerConfiguration 用于自定义圆角或相对于容器自动适配的行为(例如 .containerRelative),使玻璃在接近容器角时自动改变角半径。注意:beta 迭代中该 API 的表现可能有调整
容器/合并(UIGlassContainerEffect) :支持把多个 glass 元素放进一个容器进行合成与"合并"动画(小玻璃靠近时会像水滴合并),用 UIGlassContainerEffect + UIVisualEffectView 进行组织
示例代码
swift
复制代码
final class GlassEffectViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
makeGlassContainers()
makeLiquidGlassExample()
}
func makeGlassContainers() {
// 创建容器 effect(多个 glass 元素会合并视觉)
let container = UIGlassContainerEffect()
let containerView = UIVisualEffectView(effect: container)
containerView.frame = view.bounds
containerView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
view.addSubview(containerView)
// 创建两个玻璃子项
func makeGlassElement(frame: CGRect, tint: UIColor) -> UIVisualEffectView {
let e = UIGlassEffect(style: .regular)
e.tintColor = tint
e.isInteractive = **false******
let v = UIVisualEffectView(effect: e)
v.frame = frame
v.layer.cornerRadius = frame.height / 2
v.clipsToBounds = **true******
return v
}
let a = makeGlassElement(frame: CGRect(x: 60, y: 200, width: 120, height: 50), tint: .systemBlue)
let b = makeGlassElement(frame: CGRect(x: 180, y: 200, width: 120, height: 50), tint: .systemPink)
containerView.contentView.addSubview(a)
containerView.contentView.addSubview(b)
}
// 在 UIViewController 中示例
**func** makeLiquidGlassExample() {
guard #available(iOS 26.0, *) else {
// 回退:普通模糊
let blur = UIBlurEffect(style: .systemMaterial)
let blurView = UIVisualEffectView(effect: blur)
blurView.frame = CGRect(x: 40, y: 120, width: 240, height: 72)
blurView.layer.cornerRadius = 12
blurView.clipsToBounds = true
view.addSubview(blurView)
return
}
// 创建玻璃 effect(可设置 style)
**let** glassEffect = UIGlassEffect(style: .regular)
glassEffect.tintColor = .systemGray5
glassEffect.isInteractive = true // 启用交互感
// 使用 UIVisualEffectView 承载 effect
let glassView = UIVisualEffectView(effect: glassEffect) // 先为 nil,稍后动画 materialize
glassView.frame = CGRect(x: 40, y: 120, width: 240, height: 72)
glassView.layer.cornerRadius = 12
glassView.clipsToBounds = true
// 在 contentView 添加内容(label 会自动成为 vibrant)
let label = UILabel(frame: glassView.contentView.bounds.insetBy(dx: 12, dy: 8))
label.autoresizingMask = [.flexibleWidth, .flexibleHeight]
label.text = "Liquid Glass Button"
label.textAlignment = .center
label.textColor = .label
glassView.contentView.addSubview(label)
view.addSubview(glassView)
}
}
UIImageView Symbol Animations:drawOn/drawOff
详细介绍
场景 : 更具"手绘描边感"的开关式动画效果展示。
要点 : 使用 addSymbolEffect(.drawOn/.drawOff, options: .speed(_))
,搭配合适的 SymbolConfiguration
。
示例代码
swift
复制代码
import UIKit
final class SymbolDrawViewController: UIViewController {
private let imageView: UIImageView = {
let cfg = UIImage.SymbolConfiguration(pointSize: 96, weight: .thin)
return UIImageView(image: UIImage(systemName: "bolt", withConfiguration: cfg))
}()
override func viewDidLoad() {
super.viewDidLoad()
imageView.center = view.center
imageView.frame.size = CGSize(width: 160, height: 160)
view.addSubview(imageView)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
imageView.addSymbolEffect(.drawOff, options: .speed(0.12))
DispatchQueue.main.asyncAfter(deadline: .now() + 1.2) {
self.imageView.addSymbolEffect(.drawOn, options: .speed(0.12))
}
}
}
UIView 圆角配置 UICornerConfiguration(可动画)
详细介绍
能力点 : 通过 cornerConfiguration
以组合/胶囊/均匀/单边等方式定义圆角,且可被动画过渡。
用法 : capsule()
、uniformCorners(radius:)
、corners(topLeftRadius:...)
、uniformEdges(leftRadius:rightRadius:)
。
示例代码
swift
复制代码
import UIKit
final class CornerConfigViewController: UIViewController {
private let demo = UIView(frame: CGRect(x: 140, y: 200, width: 120, height: 120))
override func viewDidLoad() {
super.viewDidLoad()
demo.backgroundColor = .systemBlue
demo.cornerConfiguration = .uniformCorners(radius: 12)
view.addSubview(demo)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
UIView.animate(withDuration: 1) {
self.demo.cornerConfiguration = .corners(topLeftRadius: 6, topRightRadius: 24, bottomLeftRadius: 24, bottomRightRadius: 6)
}completion: { _ in
UIView.animate(withDuration: 1) {
self.demo.cornerConfiguration = .capsule()
}completion: { _ in
UIView.animate(withDuration: 1) {
self.demo.cornerConfiguration = .uniformEdges(leftRadius: 25, rightRadius: 29)
}completion: { _ in
UIView.animate(withDuration: 1) {
self.demo.cornerConfiguration = .uniformEdges(topRadius: 12, bottomRadius: 20)
}
}
}
}
}
}
动画选项 .flushUpdates(自动追踪变更)
详细介绍
能力点 : .flushUpdates
自动追踪 @Observable
数据或 AutoLayout 约束变更并添加动画,无需手动 layoutIfNeeded()
。
建议 : 数据驱动优先;必要时配合 UIViewPropertyAnimator
。
示例代码
swift
复制代码
import UIKit
@Observable final class Model { var bg: UIColor = .systemGray }
final class FlushUpdatesViewController: UIViewController {
private let box = UIView()
private var w: NSLayoutConstraint!
private var h: NSLayoutConstraint!
private let model = Model()
override func viewDidLoad() {
super.viewDidLoad()
box.backgroundColor = .systemRed
box.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(box)
w = box.widthAnchor.constraint(equalToConstant: 80)
h = box.heightAnchor.constraint(equalToConstant: 80)
NSLayoutConstraint.activate([
w, h,
box.centerXAnchor.constraint(equalTo: view.centerXAnchor),
box.centerYAnchor.constraint(equalTo: view.centerYAnchor)
])
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
view.backgroundColor = model.bg
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
UIView.animate(withDuration: 1.0, delay: 0, options: .flushUpdates) {
self.model.bg = .systemBlue
} completion: { _ in
_ = UIViewPropertyAnimator.runningPropertyAnimator(withDuration: 1.0, delay: 0, options: .flushUpdates) {
self.w.constant = 220
self.h.constant = 220
}
}
}
}
UITabBarController 最小化行为与底部辅助视图
详细介绍
能力点 :
tabBarMinimizeBehavior = .onScrollDown
:向下滚动时仅保留首个 Tab 与搜索 Tab 图标,中部显示 UITabAccessory
。
bottomAccessory
:为 TabBar 上方增设工具条等辅助视图。
示例代码
swift
复制代码
import UIKit
final class TabsVC: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
tabs.append(configTab(title: "聊天", image: "message", id: "chats"))
tabs.append(configTab(title: "通讯录", image: "person.2", id: "contacts"))
tabs.append(configTab(title: "发现", image: "safari", id: "discover"))
tabs.append(configTab(title: "我", image: "person", id: "me"))
tabs.append(configSearchTab(title: "搜索"))
selectedTab = tabs.last
tabBarMinimizeBehavior = .onScrollDown
bottomAccessory = UITabAccessory(contentView: UIToolbar())
}
private func configTab(title: String, image: String, id: String) -> UITab {
UITab(title: title, image: UIImage(systemName: image), identifier: id) { _ in
let vc = UIViewController()
let scroll = UIScrollView(frame: UIScreen.main.bounds)
scroll.backgroundColor = .secondarySystemBackground
scroll.contentSize = CGSize(width: UIScreen.main.bounds.width, height: 1600)
vc.view.addSubview(scroll)
return UINavigationController(rootViewController: vc)
}
}
private func configSearchTab(title: String) -> UISearchTab {
UISearchTab { _ in
let vc = UIViewController()
vc.view.backgroundColor = .systemBackground
return UINavigationController(rootViewController: vc)
}
}
}
iPadOS Menu Bar(UIMainMenuSystem)
详细介绍
场景 : iPadOS 具备 macOS 风格菜单栏,支持注入快捷键与自定义 UIMenu
。
要点 : 通过 UIMainMenuSystem.shared.setBuildConfiguration
在运行时构建或替换菜单层级。
示例代码
swift
复制代码
import UIKit
final class MenuBarViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let config = UIMainMenuSystem.Configuration()
UIMainMenuSystem.shared.setBuildConfiguration(config) { builder in
let refresh = UIKeyCommand(input: "R", modifierFlags: [.command], action: #selector(self.refresh))
refresh.title = "Refresh"
refresh.image = UIImage(systemName: "arrow.clockwise")
builder.insertElements([refresh], beforeMenu: .about)
let sort = UIMenu(title: "Sort", children: [
UICommand(title: "By Name", action: #selector(self.sortByName)),
UICommand(title: "By Date", action: #selector(self.sortByDate))
])
builder.insertSibling(sort, afterMenu: .help)
}
}
@objc private func refresh() { view.backgroundColor = .systemTeal }
@objc private func sortByName() { view.backgroundColor = .systemGreen }
@objc private func sortByDate() { view.backgroundColor = .systemOrange }
}
Update Properties 轻量 UI 更新
详细介绍
能力点 : UIViewController.updateProperties()
/ UIView.updateProperties()
用于不触发布局的轻量 UI 更新。
适合 : 修改文本/颜色/可见性这类不需要触发 layoutSubviews()
的更新。
补充 : setNeedsUpdateProperties()
可手动请求一次更新;与 @Observable
协同可自动追踪。
示例代码
swift
复制代码
import UIKit
@Observable final class BannerModel { var text = "Hello"; var color: UIColor = .label }
final class UpdatePropsViewController: UIViewController {
private let label = UILabel()
private let model = BannerModel()
override func viewDidLoad() {
super.viewDidLoad()
label.font = .systemFont(ofSize: 40, weight: .bold)
label.textAlignment = .center
label.frame = CGRect(x: 0, y: 0, width: view.bounds.width, height: 100)
label.center = view.center
view.addSubview(label)
}
override func updateProperties() {
super.updateProperties()
label.text = model.text
label.textColor = model.color
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
model.text = "iOS26"
model.color = .systemBlue
// 手动触发一次
setNeedsUpdateProperties()
}
}
UIKit 支持 @Observable(自动追踪 UI 更新)
详细介绍
概念 : @Observable
是一个 属性包装器(Property Wrapper) ,用于标记类或结构体,使其内部属性 自动可观察(observable) 。被标记的对象会自动生成 可监听的事件 ,UI 或其他订阅者可以自动响应属性变化,而不必手动发布通知。类似 Combine 的 @Published + ObservableObject,但不依赖 Combine,更加轻量和原生。
能力点 : UIKit 直接追踪 @Observable
类实例属性变化,自动驱动 layoutSubviews()
/viewWillLayoutSubviews()
与 .flushUpdates
动画。
兼容性 : 向下可至 iOS 18,需在 Info.plist
添加 UIObservationTrackingEnabled=YES
。
示例代码
swift
复制代码
import UIKit
@Observable final class PhoneModel { var name: String; var os: String; init(name: String, os: String) { self.name = name; self.os = os } }
final class PhoneCell: UITableViewCell {
var model: PhoneModel? { didSet { setNeedsLayout() } }
private let nameLabel = UILabel(); private let osLabel = UILabel()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
[nameLabel, osLabel].forEach { contentView.addSubview($0) }
nameLabel.font = .boldSystemFont(ofSize: 22); osLabel.font = .systemFont(ofSize: 16)
}
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
override func layoutSubviews() {
super.layoutSubviews()
nameLabel.frame = CGRect(x: 20, y: 12, width: bounds.width - 40, height: 28)
osLabel.frame = CGRect(x: 20, y: 44, width: bounds.width - 40, height: 22)
nameLabel.text = model?.name; osLabel.text = model?.os
}
}
final class PhoneListVC: UIViewController, UITableViewDataSource {
private let table = UITableView(frame: .zero, style: .plain)
private let data = [
PhoneModel(name: "iPhone 16", os: "iOS 18"),
PhoneModel(name: "iPhone 16 Pro", os: "iOS 18")
]
override func viewDidLoad() {
super.viewDidLoad()
table.dataSource = self; table.rowHeight = 78
table.register(PhoneCell.self, forCellReuseIdentifier: "cell")
table.frame = view.bounds; view.addSubview(table)
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
self.data[0].name = "iPhone 17"; self.data[0].os = "iOS 26"
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { data.count }
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! PhoneCell
cell.model = data[indexPath.row]
return cell
}
}
强类型通知(NotificationCenter.MainActorMessage/AsyncMessage)
详细介绍
能力点 : 编译期类型检查替代字符串通知名 + userInfo
方式,提升线程/类型安全。
两类 : 主线程消息 MainActorMessage
与异步消息 AsyncMessage
。
示例代码
swift
复制代码
import UIKit
public final class NotifySubject { static let shared = NotifySubject() }
public struct TitleChanged: NotificationCenter.MainActorMessage {
public typealias Subject = NotifySubject
public static var name: Notification.Name { .init("TitleChanged") }
let title: String
}
final class TypedNotificationVC: UIViewController {
private var token: NotificationCenter.ObservationToken!
private let label = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
label.frame = CGRect(x: 0, y: 0, width: view.bounds.width, height: 60)
label.center = view.center; label.textAlignment = .center; view.addSubview(label)
token = NotificationCenter.default.addObserver(of: NotifySubject.shared, for: TitleChanged.self) { [weak self] msg in
self?.label.text = msg.title
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
Task {
//子线程发送,在主线程收到
NotificationCenter.default.post(TitleChanged(title: "Updated"), subject: NotifySubject.shared)
}
}
deinit { NotificationCenter.default.removeObserver(token) }
}