UIKit 自动特征追踪详解

97 阅读7分钟

UIKit 自动特征追踪详解

iOS 18 为 UIKit 框架引入了多项重要更新,其中 自动特征追踪(Automatic Trait Tracking) 是一项显著提升开发效率和应用性能的功能。本文将深入探讨自动特征追踪的工作原理、使用方法、优势,并通过实际代码示例展示如何在实际项目中应用此特性。

1. 自动特征追踪概述

自动特征追踪是 iOS 18 中 UIKit 的一项新功能,它旨在简化特征(Trait)变化的处理过程。特征系统在 UIKit 中用于在应用层次结构中向视图控制器和视图传播数据,如水平尺寸类别(horizontalSizeClass)、首选内容尺寸类别(preferredContentSizeCategory)等。在 iOS 18 之前,开发者需要手动注册特征变更通知,并在特征变化时手动调用如 setNeedsLayoutsetNeedsDisplay 等方法來更新视图。自动特征追踪通过自动记录在特定更新方法中访问的特征,并在这些特征变化时自动触发相应的失效操作,从而减少了样板代码和潜在错误。

2. 工作原理与机制

自动特征追踪的核心在于其自动依赖关系管理。当 UIKit 调用支持的更新方法(如 layoutSubviews(), draw(_ rect: CGRect), updateViewConstraints() 等)时,它会记录在该方法中访问的特征。随后,当任何这些特征的值发生变化时,UIKit 会自动执行与该方法关联的失效操作(例如,如果是在 layoutSubviews 中访问的特征变化,则自动调用 setNeedsLayout())。

2.1 支持的方法

iOS 18 的自动特征追踪支持以下常见的视图和视图控制器更新方法:

  • layoutSubviews()

  • draw(_ rect: CGRect)

  • updateViewConstraints()

  • 以及其他常见的更新方法(具体请参考官方文档)。

2.2 示例说明

考虑一个自定义 UIView 子类,它重写了 layoutSubviews() 方法,并根据当前的特征集合(如 horizontalSizeClass)决定布局类型:


class AdaptiveView: UIView {

override func layoutSubviews() {

super.layoutSubviews()

// 读取 horizontalSizeClass 特征

let sizeClass = traitCollection.horizontalSizeClass

if sizeClass == .compact {

// 应用紧凑布局

applyCompactLayout()

} else {

// 应用常规布局

applyRegularLayout()

}

}

private func applyCompactLayout() {

// 实现紧凑布局逻辑

}

private func applyRegularLayout() {

// 实现常规布局逻辑

}

}

在 iOS 18 之前,你需要手动注册特征变更通知,并在特征变化时调用 setNeedsLayout()


// iOS 17 及更早版本的做法

class AdaptiveViewOld: UIView {

override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {

super.traitCollectionDidChange(previousTraitCollection)

if traitCollection.horizontalSizeClass != previousTraitCollection?.horizontalSizeClass {

setNeedsLayout() // 手动触发布局更新

}

}

override func layoutSubviews() {

super.layoutSubviews()

// 同样的布局逻辑

}

}

在 iOS 18 中,由于自动特征追踪,你无需再编写 traitCollectionDidChange 中的手动检查代码。UIKit 会自动在 layoutSubviews 中记录对 horizontalSizeClass 的访问,并在其变化时自动调用 setNeedsLayout()

3. 如何使用自动特征追踪

使用自动特征追踪非常简单,因为它在支持的更新方法中默认启用。你只需要确保在这些方法中访问特征,UIKit 会自动处理后续的失效操作。

3.1 基本使用

  1. 在支持的更新方法中访问特征:例如,在 layoutSubviews() 中读取 traitCollection 的属性。

  2. 让 UIKit 处理剩余工作:当特征变化时,UIKit 会自动标记视图为需要更新。

3.2 禁用自动特征追踪

虽然自动特征追踪通常有益,但在某些边缘情况下,你可能希望禁用它们。可以通过在 Info.plist 中设置 UIObservationTrackingEnabledNO 来全局禁用(但 iOS 26 开始默认启用)。不过,iOS 18 中此功能默认激活,且通常不建议禁用。

4. 自动特征追踪的优势

4.1 简化代码

自动特征追踪消除了手动注册特征变更通知和调用失效方法的需要,使代码更简洁、更易于维护。

4.2 减少错误

手动管理特征依赖关系容易出错,例如,可能遗漏某些特征变化或过度无效化。自动追踪确保了依赖关系的准确性和高效性。

4.3 性能优化

UIKit 仅追踪实际在更新方法中使用的特征,避免了不必要的无效化操作,从而提升了性能。

4.4 更好的兼容性

与 SwiftUI 的互操作性得到增强,因为 SwiftUI 的视图和 UIKit 视图在特征处理上更加一致。

5. 实际应用案例

5.1 在集合视图和表视图中的应用

iOS 18 还对 UICollectionViewUITableView 的 API 进行了更新,使其更好地利用自动特征追踪。现在,所有在 UICollectionView 列表部分和 UITableView 中的视图都设置了 listEnvironment 特征。UIListContentConfigurationUIBackgroundConfiguration 现在可以利用这个新特征。当为新的状态更新时,它们会调整其属性以匹配来自配置状态特征集合的列表环境,这意味着在配置单元格时无需知道列表的样式。

例如,Files 应用在 iOS 18 中可以简化其单元格配置代码:


// iOS 18 中的简化配置

func makeConfiguration(for location: FileLocation) -> UIContentConfiguration {

let contentConfig = UIListContentConfiguration.cell()

contentConfig.text = location.name

contentConfig.secondaryText = location.details

let backgroundConfig = UIBackgroundConfiguration.listCell()

// 配置会自动根据列表环境特征调整外观

return contentConfig

}

5.2 与 SwiftUI 的互操作

iOS 18 进一步改善了 SwiftUI 和 UIKit 之间的互操作性。你可以在 UIKit 视图内部使用 SwiftUI 动画来动画化视图,并且现在可以更轻松地协调两个框架之间的手势。自动特征追踪与此密切相关,因为它提供了一种一致的方式来处理特征变化,无论视图是来自 UIKit 还是 SwiftUI。

6. 与旧 API 的对比和迁移建议

6.1 旧方法:手动特征变更通知

在 iOS 17 及更早版本中,处理特征变化通常需要重写 traitCollectionDidChange(_:) 方法,并手动检查特定特征的变化:


// 旧方法:手动检查特征变化

override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {

super.traitCollectionDidChange(previousTraitCollection)

if traitCollection.horizontalSizeClass != previousTraitCollection?.horizontalSizeClass {

setNeedsLayout()

}

if traitCollection.preferredContentSizeCategory != previousTraitCollection?.preferredContentSizeCategory {

setNeedsDisplay()

}

}

6.2 新方法:依赖自动特征追踪

在 iOS 18 中,你只需要在支持的更新方法中访问特征,其余工作由 UIKit 自动完成:


// 新方法:自动特征追踪

override func layoutSubviews() {

super.layoutSubviews()

let sizeClass = traitCollection.horizontalSizeClass

let contentSizeCategory = traitCollection.preferredContentSizeCategory

// 根据特征更新布局或绘制

updateLayout(for: sizeClass, contentSizeCategory: contentSizeCategory)

}

6.3 迁移建议

  • 审查现有代码:查找重写了 traitCollectionDidChange 的地方,检查是否仅用于触发布局或显示更新。

  • 移除手动无效化:如果只是为了调用 setNeedsLayoutsetNeedsDisplay,可以移除这些手动代码。

  • 确保特征访问在正确的方法中:将特征读取逻辑移动到 layoutSubviewsdraw 或其他支持的方法中。

  • 测试:由于自动特征追踪可能改变无效化的时机,务必进行全面测试。

7. 最佳实践和注意事项

7.1 最佳实践

  • 将特征读取逻辑集中放在支持的更新方法中:这确保了自动追踪能够正确工作。

  • 利用列表环境特征:对于集合视图和表视图,使用 UIListContentConfigurationUIBackgroundConfiguration 的新构造函数(如 listCelllistHeaderlistFooter),它们会自动适应列表样式。

  • 结合 SwiftUI 使用:在混合项目中,利用改进的互操作性,确保特征变化在 UIKit 和 SwiftUI 视图之间一致地传递。

7.2 注意事项

  • 性能影响:虽然自动追踪优化了性能,但确保在更新方法中避免不必要的昂贵计算。

  • 不支持的方法:自动特征追踪仅适用于特定方法。在其他方法中访问特征不会触发自动无效化。

  • 复杂逻辑:对于非常复杂的特征依赖关系,仍需仔细设计,但自动追踪应能处理大多数常见情况。

8. 未来发展方向

iOS 18 的自动特征追踪是 UIKit 现代化的一部分。随着 iOS 26 的规划,Apple 正在进一步扩展这些概念,例如引入 updateProperties() 方法作为通用的更新机制,它支持自动特征和观察追踪(Automatic Observation Tracking),允许更细粒度的属性更新而不强制布局。这表明 Apple 致力于简化状态管理和视图更新,让开发者更专注于构建出色的用户体验。

总结

iOS 18 中引入的自动特征追踪是 UIKit 的一个重大改进,它通过自动化特征变化的处理,简化了开发流程,减少了样板代码,并提高了应用性能。通过消除手动注册特征变更通知和调用失效方法的需要,开发者可以编写更简洁、更健壮的代码。随着 UIKit 继续演进,与 SwiftUI 的深度集成以及更多自动化功能的加入,iOS 开发将变得更加高效。

原文:xuanhu.info/projects/it…