大多数浏览器和
Developer App 均支持流媒体播放。
-
UIKit 的新功能
借助 UIKit 最新推出的多种 API,对你的 App 进行现代化改造,这些更新包括增强的菜单栏支持、自动观察跟踪、全新 UI 更新方法,以及动画效果改进。我们还将介绍如何在 UIKitApp 中包含 SwiftUI 场景,并探讨 SF Symbols、HDR 颜色选择器等内容。
章节
- 0:00 - Introduction
- 0:59 - New design system
- 2:29 - Containers and adaptivity
- 3:21 - The menu bar
- 9:58 - Architectural improvements
- 10:21 - Automatic observation tracking
- 12:33 - New UI update method
- 15:45 - Improvements to animations
- 17:45 - Scene updates
- 18:55 - HDR Color support
- 20:38 - Swift notifications
- 21:20 - Migrate to a scene-based life cycle
- 22:40 - OpenURL support for file URLs
- 23:17 - SF Symbols 7
- 25:13 - Next steps
资源
相关视频
WWDC25
WWDC24
WWDC23
WWDC21
-
搜索此视频…
大家好 欢迎观看 “UIKit 的新功能”讲座 我是 Dima UIKit 团队的工程经理 从 iOS 和 iPadOS 到 Apple tvOS、 visionOS 和 Mac Catalyst UIKit 仍然是你 App 的基础 现在推出了更多增强功能 首先 我将简要介绍 UIKit 针对 全新设计系统提供的支持 然后 我将介绍如何改进 App 的内容 以便顺畅适配不同的设备和屏幕形状 之后 我将介绍适用于菜单栏的 全新 UIKit API 菜单栏这一为人熟知的 macOS 元素 现已登陆 iPadOS! 我将深入介绍 UIKit 中 架构方面的核心改进 并重温一些关键的基础知识 最后 我将总结我们在通用框架 方面实现的更广泛优化 全新设计系统为系统材质和控件 带来鲜活生动的外观 它的核心是一种 全新材质:Liquid Glass 它是半透明的并且生动鲜活 具有镜面高光和折射等效果 从各种栏和搜索字段到 提醒、弹出窗口和分屏浏览 UIKit 标准组件在新材质的加持下 焕然一新
导航过渡现在可实现流畅且 可中断的效果 为你的 App 赋予更灵敏的体验
用户可以开始与内容互动 而无需等待动画结束
为了便于将你自己的 UI 升级为 全新设计 我们推出了一些新工具 比如背景扩展视图 这个视图让你的内容能够在 边栏的大玻璃面板下显示 从而保持视觉上的连贯性
我们还提供了一种适用于你自己的 自定组件的玻璃材质 和一种全新的滚动边缘效果 让你的内容在玻璃面板下滚动时 能够呈现优雅的淡入淡出效果 从而提升栏按钮和 其他控件的可视性
要获取全面的实际操作指南来 了解 UIKit App 外观的更新 请观看视频“使用全新设计系统 构建 UIKit App” 要深入了解全新设计本身 请观看“了解全新设计系统” 接下来 我将简要介绍如何 改进 App 的内容 以便顺畅适配不同的设备和屏幕形状 iOS 26 为 UISplitViewController 提供了一流的检查器支持 检查器提供了所选内容的更多详情 例如 “预览”使用检查器在 照片旁边的次要列中 显示了元数据
现在 你还可以通过托移 分屏浏览控制器的分隔线 来调整栏的大小 使用指针时 它的形状会自适应调整 以指示栏可以沿什么方向来调整大小 如需进一步了解容器视图控制器 方面的改进 以及回顾布局边距和安全区域等 常规布局概念 请观看视频 “让你的 UIKit App 更加灵活” 接下来 我们将介绍菜单:iOS 26 将 macOS 中的菜单栏引入 iPad 现在 从顶部轻扫一下即可 显示 App 的完整菜单 甚至连硬件键盘都不需要! 通过这种高效方式 能够快速访问 App 中的功能
菜单栏支持所有菜单功能 比如 图像、子菜单、内联部分 复选标记等 它应该会显示你 App 中的所有命令 甚至包括那些没有键盘快捷键的命令 并且它会使不可用命令保持 可见但被停用的状态 这样可以确保用户可以发现你的 App 能够完成的所有操作
App 仍然使用 UIMenuBuilder 来 自定自身的主菜单 在 iOS 26 中 UIKit 推出了 多个新的 API 来打造 更出色的菜单栏 我将从主菜单系统配置开始介绍 这个 API 可让 App 自定主菜单中 最初提供哪些系统命令 当你使用 configuration API 时 你将选择为 App 加入 更多预置的本地化菜单元素 比如用于切换检查器的新命令 这个 API 让你还可以提前 声明要包含或省略的项目 你还可以深度配置各个元素组以及 设置它们的样式 例如 根据你的 App 需求来 优化查找命令 最后 提前提供一个 UIMenuBuilder 块来添加自定项目 从而让你的 App 以及它的共享扩展 使用相同的代码来定义 受支持的键盘快捷键 这是一个有关使用主菜单系统配置 的示例 首先 我创建一个配置对象 然后 我指定我的 App 在主菜单中默认需要哪些命令 例如 我将声明对系统打印命令 的支持
我将选择不使用某些默认命令 例如 用于切换检查器面板的新命令 通过配置 我可以为默认命令 指定通用样式 比如将系统查找命令转换为 单个搜索项 这非常适合照片或音乐 App 这类 App 侧重于搜索内容 而不是搜索文本 最后 我可以在主菜单系统上 设置配置 以便它使用初始的一组首选元素 进行构建 我还可以选择提供一个构建处理程序 以便调用它而不是 buildMenuWithBuilder 这个处理程序将提供对 UIMenuBuilder 的访问 后者已在 iOS 26 中升级 提供了更强大的便利方法 更快的性能和改进的诊断
请注意 设置配置将触发菜单栏 的重新构建 理想情况下 你的 App 应该只设置一次配置 并应尽早设置 例如 application(_:didFinishLaunchingWithOptions:) iOS 和 macOS 26 在菜单栏中 推出了更多标准操作和菜单 performClose 会映射到 Cmd-W 并默认关闭你的窗口场景 但你还可以让它关闭 App 中的其他内容 例如网页浏览器中的标签页 菜单命令“从剪贴板新建”用于 通过粘贴板的内容创建文档 而不会触发粘贴提醒 iOS 26 中推出的 newItem 菜单 是放置它的好地方 用于文本对齐、边栏切换和 检查器切换的标准操作 现在也已公开 以供你的 App 进行自定 默认情况下 按下按键时 键盘快捷键会重复触发 但你也可以通过在 UIKeyCommand 上 设置 repeatBehavior 属性来 自定这个行为
还可以使用 validateCommand 为每个响应对象设置这个属性。 这对于那些破坏性的操作 (例如按下 Delete 键删除电子邮件) 非常重要 这样你就不会意外地 反复触发这类操作 某些情况下 菜单栏的某些部分需要 根据聚焦项目或窗口场景来 显示动态内容 例如 浏览 Safari 浏览器 等 App 可能会 在“History”菜单中显示当前 浏览配置文件的历史记录
要支持这项功能 请使用基于焦点的 新延迟菜单元素 这个元素会从响应器链填充它的元素 构建你的主菜单时 请创建一个使用 焦点的 UIDeferredMenuElement 并为它提供一个标识符来进行区分 然后将它插入主菜单中
当需要实现延迟元素时 UiKit 会遍历响应器链 直到它找到一个可以提供项目的 响应器 在这个示例中 浏览器视图控制器会覆盖 providerForDeferredMenuElement 以提供当前配置文件的历史记录项目
它会检查延迟元素的标识符以找到 browserHistory 元素 并返回一个 Provider 以载入 历史记录菜单项 基于焦点的延迟元素是使用无键命令 确保菜单栏保持最新状态的绝佳方式 无需在主菜单系统上执行 开销很大的重新构建 除了 App 的自定项目外 系统还会自动提供几个菜单条目 你的 App 会获得一个键盘快捷键 用于在“设置”App 中 打开它的设置 对于基于文档的 App “Open Recent”菜单将填充 最近打开的文档 系统还会为窗口补充平铺命令 包括列出 App 的所有已打开的场景 填充每个场景的标题 以帮助用户区分它们 最后 在构建你的菜单栏时 有几件事需要注意 对于 UIKit App 将不再支持 Storyboard 中定义的菜单栏 App 不会通过 Storyboard 中 的菜单启动 因此需要以编程方式实现它们
你还应确保 App 的功能在没有 菜单栏的情况下仍可供访问 因为菜单栏并不总是存在 要进一步了解如何为你的 App 构建出色的菜单栏 请观看“提升 iPad App 设计” 如需简要了解 UIKit 主菜单 请观看“让你的 iPad App 更上一层楼” 我们正在持续改进 UIKit 并加入一些支持现代模式、 最佳实践和更深入 SwiftUI 互操作性的功能 iOS 26 也不例外 我将和你分享一些 激动人心的全新架构改进 第一个重大改进是针对 UIKit 中的 Swift Observable 对象 提供内置支持
现在 UIKit 在其核心整合了 Swift Observation:在 layoutSubviews() 等更新方法中 它会自动追踪 你引用的任何 Observable、 连接依赖项并使正确的视图无效 而无需手动调用 setNeedsLayout 在 iOS 18 上 你可以将 UIObservationTrackingEnabled 键 添加到 Info.plist 来 回溯部署这个功能 在 iOS 26 上 它默认处于启用状态 我将介绍几个示例以展示 如何实际应用自动观察追踪功能
这里有一个邮件列表视图控制器 其中包含一个指示 未读邮件的 UILabel
它由包含两个属性的 Observable 模型对象提供支持: 一个属性是用于控制是否显示状态的 布尔值 另一个是状态字符串 在 viewWillLayoutSubviews() 中 我使用可观察模型 来更新标签的 alpha 以 显示或隐藏它 并设置它的文本 在第一个布局上 UIKit 填充了标签 得益于自动观察追踪功能 UIKit 还在 showStatus 和 statusText 上记录了依赖项 对这些属性进行任何更改都会 使视图无效 并返回 viewWillLayoutSubviews() 从而使 标签保持同步而不需要编写额外代码 接下来将再展示一个有关将 Observable 对象与 UIKit 搭配使用 的示例 这个示例突出展示了在 UICollectionView 中配置单元格时 使用自动观察追踪功能有哪些好处 我使用可观察的 ListItemModel 为每个列表单元格提供支持 这个模型中包含图标、标题和副标题 在单元格提供程序回调的内部 我使 一个单元格退出队列、抓取它的模型 并分配一个 configurationUpdateHandler 由于这个处理程序支持对象追踪 因此 UIKit 会自动 为我在它内部使用的任何 Observable 对象创建依赖关系
在处理程序中 我使用可观察的 列表项模型 填充并应用了一个列表内容配置 就是这样! 现在 当单元格可见时对模型属性 所作的任何更改都会导致 UIKit 返回处理程序并为我更新单元格 由于新增了自动特征和观察追踪功能 UIKit 现在包含一个通用的更新方法 来支持这些功能
我们将一个新方法 updateProperties() 引入 UIView 和 UIViewController
它在 layoutSubviews() 之前运行 但两者是独立的:让你能够 使属性无效而无需强制进行布局 反之亦然 这样就可以避免额外的 重复处理 并实现更精细的更新
updateProperties() 会补充而不是 取代 layoutSubviews() 可使用它填充内容、应用样式或 配置行为
它会自动追踪你读取的任何 Observable 并且你可以调用 setNeedsUpdateProperties() 来手动触发它 我将举一个具体的例子来展示 如何使用这个新方法
这里有一个 Observable BadgeModel 对象 用于储存徽章视图中显示的计数 栏按钮项目的视图控制器 由 BadgeModel 提供支持
在 updateProperties() 中 我针对 栏按钮项目使用了新的徽章 API 并直接从模型中提取计数 现在 每当可观察模型对象更改时 都会导致 updateProperties() 运行和更新徽章
通过使用 updateProperties() 而不是 layoutSubviews() 来配置 视图 我可以避免对调整大小等 不相关的事件重新运行代码 从而减少了不必要的工作 并提升了性能 为更好地了解 updateProperties() 如何与其他更新方法搭配使用 我将介绍 UIKit 的更新流程 是如何运作的
这个示意图展示了 UiKit 在屏幕上 显示视图之前如何更新视图 首先是布局流程 UIKit 会从上到下遍历 视图层次结构、 更新每个视图的特征 然后调用 layoutSubviews() 如果这个流程导致其他视图需要布局 那么布局流程将重复进行 直到所有内容均完成布局
在布局流程完成后 UIKit 将执行显示流程 针对每个视图调用 draw 方法 并重复进行 直到所有视图 均不再需要显示为止 在这两个流程都完成后 便可以渲染下一帧 并显示在屏幕上
这里展示了新 updateProperties() 回调如何融入这个流程 在自上而下的布局流程中 UIKit 会在更新特征之后、在执行 layoutSubviews() 之前运行 updateProperties() 你可以将 layoutSubviews() 看作被分成了两个阶段: 先是属性更新 然后是通常的布局逻辑 由于特征集合在 updateProperties() 运行之前 已更新 因此你可以在那里读取它 由于它总是在 layoutSubviews() 之 前执行 因此你可以使它内部的布局 失效 之后布局流程将立即运行
为了补充新的观察追踪功能和新的 updateProperties() 方法 我们改进了动画在 UIKit 中的 工作方式
开始之前 我将介绍手动更新在 iOS 18 及更早版本中的工作方式 在 UIView 动画闭包中 首先在 Observable 对象上 设置新的值 然后 针对依赖这些对象的视图 调用 layoutIfNeeded() 这两个步骤都是必要的因为 第一步会导致相关内容失效 第二步会执行更新并创建动画 手动维护属性和视图依赖性容易出错 并可能会导致更新或动画数量 太多或太少 iOS 26 有一个用于 UIViews 的 新动画选项 称为 flushUpdates 启用这个选项后 UIKit 会在 动画开始之前应用待处理的更新 然后在动画结束时再次应用 因此无需再调用 layoutIfNeeded() 要让 flushUpdates 正常工作 应仅在动画闭包内部进行 使状态失效的更改 我将展示一个有关 如何使用 flushUpdates 的示例 首先 将 flushUpdates 作为选项 传递给 UIView.animate 然后对闭包内的 Observable 对象进行更改 在更新方法中使用这个 Observable 对象的视图 都会自动执行必要的更新 flushUpdates 不仅限于 由 Observable 驱动的动画
这个示例展示了如何使用它 自动为自动布局约束更改 添加动画效果 在 flushUpdates 闭包中 我将一个新的常量 设置为某个现有约束 并激活和停用其他约束 关联视图会自动以动画效果移动到 新位置并调整大小 现在 我将介绍如何在同一 App 中 更顺畅地使用 SwiftUI 和 UIKit 使用新的委托协议的 UIKit 应用程序现在支持 SwiftUI 场景 这支持增量采用 SwiftUI 并且还让 UIKit App 能够 在 visionOS 上利用 沉浸式空间和空间容器
假设有一个呈现禅意花园场景的 冥想 App:这个场景在 iPhone 和 iPad 上呈现在标准 2D 窗口中 在 Vision OS 上则作为沉浸式空间呈现 为了呈现这个沉浸式空间 我使用根 SwiftUI 场景实现了新的 UIHostingSceneDelegate 协议
使用托管场景委托就像使用 任何其他场景委托一样 只需在新场景连接时 在 UISceneConfiguration 上 设置 delegateClass 类型
通过传递场景标识符 你可以通过编程方式从托管委托 请求一个特定的 SwiftUI 场景
在这个示例中 我请求了 沉浸式禅意花园空间 最后 我将介绍 UIKit 中 的一些常规增强功能 首先从 HDR 渲染的改进说起 在 iOS 26 中 HDR 不再只用于图像 领域 颜色也得到了同样的处理 因此 你可以突出用户界面或 实现新的体验
UIColor 现在让你可以指定 SDR 基本色和曝光值 并会自动根据显示设备的性能 来自动调整亮度 这里创建了红色 HDR 并将其曝光值设置为 SDR 峰值白度的 2.5 倍 现在 你还可以在 UIColorPickerViewController 和 UIColorWell 中启用 HDR 颜色选择 为此 请将最大曝光设置为一个 由 App 的渲染功能决定的值 在这里 我将颜色选择器的 最大线性曝光设置为 SDR 峰值白度的两倍
在 iOS 18 中 UIImageView 会 智能地将 HDR 回退到 SDR 确保 UI 中的关键内容引人注目 在 iOS 26 中 这个行为已扩展到 视频 并且你自己的自定内容 也可以参与 使用新的 UITraitHDRHeadroomUsage 特征来 监控你的 HDR 内容什么时候 应回退到 SDR 要进一步了解 HDR 请观看 “利用 HDR 为 App 打造 动态图像体验” 和“在你的 App 中 支持 HDR 图像”
在 NSNotification.Name API 的 基础上 iOS 26 中的 UIKit 现在 将每个通知表示为专用的 NotificationCenter.Message 类型 这为你提供了一个用于注册观察器 和检索事件详细信息的强类型值 这个示例展示了如何在 出现键盘时调整布局 我注册了 keyboardWillShow 通知类型 然后在处理程序中 我直接从 message 中 调出动画持续时间和键盘帧 最后 我使用这些值为约束添加 动画效果 无需查询 userInfo 或手动进行类型转换 随着每次发布 不断演化的 UIKit 都会为你带来符合当下最佳实践的 现代化、强大的 API 当你采用这些 API 时 我们正在逐步弃用传统方法
UIScene 的采用会让你的 App 具备便携性和高度的灵活性 因此我们正在逐步弃用许多围绕 UIApplication 的 API
旧 UIApplicationDelegate 回调和 UIApplicationLaunchOptionKeys 不再适用 并且只有 UIWindow 的 init(windowScene:) 初始化程序保留下来 所有其他初始化程序都已弃用 在 iOS 26 之后的版本中 使用最 新 SDK 构建的任何 UIKit App 都 需要使用 UIScene 生命周期 否则将无法启动
请在任何地方采用 UIScene 生命 周期 而不仅仅是在多窗口 App 中 如需详细了解如何完成这一操作 请阅读技术说明: “迁移到 UIKit 基于场景的 生命周期” 如需进一步了解如何充分提高 App 的灵活性 包括一些 有助于你从 UIRequiresFullScreen 进行迁移的新 API 请观看视频 “让你的 UIKit App 更加灵活”
使用各种文档类型的 App 通常需要启动外部检视器 在 iOS 26 中 现有的 openURL 方法 现在接受文件 URL 因此 你现在可以传递不受 你的 App 原生支持的文档 如果存在与相应文件类型 对应的默认 App 系统会启动它并传递你的 URL 如果不存在的话 openURL 将返回 false 从而为你提供一个选项来 自行处理回退 例如 使用快速预览控制器
SF Symbols 在 iOS 26 中 得到了改进 SF Symbols 7 新增了绘制符号的 功能 首先推出了两种新效果 和 Disappear 效果一样 Draw Off 使用绘制动画来隐藏符号 和 Appear 一样 Draw On 会通过 绘制来显示一个隐藏符号
Symbols 现在支持 Variable Draw 这是一个适用于变量值的新模式 这个模式会沿着路径绘制任意值 例如针对这个进度指示器进行绘制 你还可以将它与自动符号内容过渡 一起使用 以便在变量值之间创建动画效果
魔术替换过渡还可以在某些符号之间 执行特殊的绘制动画 例如 圆圈符号和填充的 勾号符号之间的这个过渡 现在会填充圆圈并绘制勾号 新的绘制动画对按钮非常有用 因此 UIKit 提供了新的 API 让你能够轻松在 UIButton 中 采用符号内容过渡
在 UIButton.Configuration 上使用 新的 symbolContentTransition 属性来指定符号内容过渡 例如 replace 当按钮的符号更改时 例如当它的选择状态切换时 UIKit 会执行过渡 SF Symbols 7 还推出了其他功能 例如新的 colorRenderingMode 选项 App 可以指定 Gradient 以使用 自动生成的渐变色而不是纯色 来为符号着色
要了解如何完全自定绘制和渐变 请观看“SF Symbols 7 的新功能” 如需回顾如何针对动画 采用符号效果 请观看 “在 App 中为符号添加动画效果”
接下来要做什么? 使用 iOS 26 SDK 编译你的 App 了解你的 App 如何响应新设计 并完善你的 UI 和样式 以便与新的美学相得益彰 使用 UISplitViewController 和 UITabBarController 等标准容器 获取支持以实现灵活的布局 使用新的菜单 API 实现 App 的菜单 采用 updateProperties 方法和 观察追踪功能 简化你的代码并提升性能 感谢观看! 我很期待看到大家如何利用这些改进 让 App 不但功能更强大 还能带来更愉悦的使用体验
-
-
4:56 - Main menu system configuration
// Main menu system configuration var config = UIMainMenuSystem.Configuration() // Declare support for default commands, like printing config.printingPreference = .included // Opt out of default commands, like inspector config.inspectorPreference = .removed // Configure the Find commands to be a single "Search" element config.findingConfiguration.style = .search
-
5:39 - Main menu system build configuration
// Main menu system configuration // Have the main menu system build using this configuration, and make custom additions. // Call this early, e.g. in application(_:didFinishLaunchingWithOptions:), and call it once UIMainMenuSystem.shared.setBuildConfiguration(config) { builder in builder.insertElements([...], afterCommand: #selector(copy(_:))) let deleteKeyCommand = UIKeyCommand(...) builder.replace(command: #selector(delete(_:)), withElements: [deleteKeyCommand]) }
-
7:01 - Keyboard shortcut repeatability
// Keyboard shortcut repeatability let keyCommand = UIKeyCommand(...) keyCommand.repeatBehavior = .nonRepeatable
-
7:43 - Focus-based deferred menu elements (App Delegate)
// Focus-based deferred menu elements extension UIDeferredMenuElement.Identifier { static let browserHistory: Self = .init(rawValue: "com.example.deferred-element.history") } // Create a focus-based deferred element that will display browser history let historyDeferredElement = UIDeferredMenuElement.usingFocus( identifier: .browserHistory, shouldCacheItems: false ) // Insert it into the app’s custom History menu when building the main menu builder.insertElements([historyDeferredElement], atEndOfMenu: .history)
-
8:06 - Focus-based deferred menu elements (View Controller)
// Focus-based deferred menu elements class BrowserViewController: UIViewController { // ... override func provider( for deferredElement: UIDeferredMenuElement ) -> UIDeferredMenuElement.Provider? { if deferredElement.identifier == .browserHistory { return UIDeferredMenuElement.Provider { completion in let browserHistoryMenuElements = profile.browserHistoryElements() completion(browserHistoryMenuElements) } } return nil } }
-
10:54 - Using an Observable object and automatic observation tracking
// Using an Observable object and automatic observation tracking @Observable class UnreadMessagesModel { var showStatus: Bool var statusText: String } class MessageListViewController: UIViewController { var unreadMessagesModel: UnreadMessagesModel var statusLabel: UILabel override func viewWillLayoutSubviews() { super.viewWillLayoutSubviews() statusLabel.alpha = unreadMessagesModel.showStatus ? 1.0 : 0.0 statusLabel.text = unreadMessagesModel.statusText } }
-
11:48 - Configuring a UICollectionView cell with automatic observation tracking
// Configuring a UICollectionView cell with automatic observation tracking @Observable class ListItemModel { var icon: UIImage var title: String var subtitle: String } func collectionView( _ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath ) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) let listItemModel = listItemModel(for: indexPath) cell.configurationUpdateHandler = { cell, state in var content = UIListContentConfiguration.subtitleCell() content.image = listItemModel.icon content.text = listItemModel.title content.secondaryText = listItemModel.subtitle cell.contentConfiguration = content } return cell }
-
13:27 - Using automatic observation tracking and updateProperties()
// Using automatic observation tracking and updateProperties() @Observable class BadgeModel { var badgeCount: Int? } class MyViewController: UIViewController { var model: BadgeModel let folderButton: UIBarButtonItem override func updateProperties() { super.updateProperties() if let badgeCount = model.badgeCount { folderButton.badge = .count(badgeCount) } else { folderButton.badge = nil } } }
-
16:57 - Using the flushUpdates animation option to automatically animate updates
// Using the flushUpdates animation option to automatically animate updates // Automatically animate changes with Observable objects UIView.animate(options: .flushUpdates) { model.badgeColor = .red }
-
17:23 - Automatically animate changes to Auto Layout constraints with flushUpdates
// Automatically animate changes to Auto Layout constraints UIView.animate(options: .flushUpdates) { // Change the constant of a NSLayoutConstraint topSpacingConstraint.constant = 20 // Change which constraints are active leadingEdgeConstraint.isActive = false trailingEdgeConstraint.isActive = true }
-
18:07 - Setting up a UIHostingSceneDelegate
// Setting up a UIHostingSceneDelegate import UIKit import SwiftUI class ZenGardenSceneDelegate: UIResponder, UIHostingSceneDelegate { static var rootScene: some Scene { WindowGroup(id: "zengarden") { ZenGardenView() } #if os(visionOS) ImmersiveSpace(id: "zengardenspace") { ZenGardenSpace() } .immersionStyle(selection: .constant(.full), in: .mixed, .progressive, .full) #endif } }
-
18:28 - Using a UIHostingSceneDelegate
// Using a UIHostingSceneDelegate func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { let configuration = UISceneConfiguration(name: "Zen Garden Scene", sessionRole: connectingSceneSession.role) configuration.delegateClass = ZenGardenSceneDelegate.self return configuration }
-
18:41 - Requesting a scene
// Requesting a scene func openZenGardenSpace() { let request = UISceneSessionActivationRequest( hostingDelegateClass: ZenGardenSceneDelegate.self, id: “zengardenspace")! UIApplication.shared.activateSceneSession(for: request) }
-
19:18 - HDR color support
// Create an HDR red relative to a 2.5x peak white let hdrRed = UIColor(red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0, linearExposure: 2.5)
-
19:50 - HDR color picking
// Support picking HDR colors relative to a // maximum peak white of 2x colorPickerController.maximumLinearExposure = 2.0
-
20:06 - Mixing SDR and HDR content
// Mixing SDR and HDR content registerForTraitChanges([UITraitHDRHeadroomUsageLimit.self]) { traitEnvironment, previousTraitCollection in let currentHeadroomLimit = traitEnvironment.traitCollection.hdrHeadroomUsageLimit // Update HDR usage based on currentHeadroomLimit’s value }
-
20:54 - Adopting Swift notifications
// Adopting Swift notifications override func viewDidLoad() { super.viewDidLoad() let keyboardObserver = NotificationCenter.default.addObserver( of: UIScreen.self for: .keyboardWillShow ) { message in UIView.animate( withDuration: message.animationDuration, delay: 0, options: .flushUpdates ) { // Use message.endFrame to animate the layout of views with the keyboard let keyboardOverlap = view.bounds.maxY - message.endFrame.minY bottomConstraint.constant = keyboardOverlap } } }
-
24:26 - Using a symbol content transition to automatically animate symbol updates
// Using a symbol content transition to automatically animate symbol updates var configuration = UIButton.Configuration.plain() configuration.symbolContentTransition = UISymbolContentTransition(.replace)
-
-
- 0:00 - Introduction
Learn about UIKit's enhancements across iOS, iPadOS, tvOS, visionOS, and Mac Catalyst. Topics include new design system support, adaptive content, iPadOS and Mac Catalyst menu bar APIs, core architectural advancements, and general framework improvements.
- 0:59 - New design system
The new design system features Liquid Glass, a translucent, dynamic material that refreshes standard UIKit components and enhances navigation transitions. New tools and effects allow you to create custom glass components and improve scroll legibility, all for a more responsive and visually continuous app experience.
- 2:29 - Containers and adaptivity
iOS 26 enhances 'UISplitViewController' with inspectors for detailed content display and dynamic column resizing, improving app adaptability across devices.
- 3:21 - The menu bar
iOS 26 introduces a menu bar to iPad, accessible via a swipe from the top, providing quick access to app functionality without a hardware keyboard. The menu bar supports various features and displays all app commands, even those without keyboard shortcuts. Customize the menu bar using new APIs, including configuring system commands, styling elements, and implementing dynamic content based on the focused item. Standard actions like 'Close', and 'New from Clipboard' are introduced. Actions for text alignment, sidebar toggling, and inspector toggling are now exposed for customization. Make sure that actions are available in your app without relying on the menu bar.
- 9:58 - Architectural improvements
New architectural improvements in iOS 26 include built-in support for Swift Observable objects in UIKit, which enhances best practices and deeper SwiftUI interoperability.
- 10:21 - Automatic observation tracking
UIKit now automatically tracks Observable objects referenced in update methods such as 'layoutSubviews', eliminating the need for manual 'setNeedsLayout' calls. This feature is enabled by default in iOS 26 and you can back-deploy it to iOS 18 with the 'UIObservationTrackingEnabled' Info.plist key. In practice, this means that changes to Observable model properties used to update UI elements, such as labels or collection view cells, automatically trigger view invalidation and reruns of the relevant update methods, keeping the UI in sync without additional code.
- 12:33 - New UI update method
UIKit introduces a new method, 'updateProperties', available in 'UIView' and 'UIViewController'. This method runs independently before 'layoutSubviews' and allows you to populate content, apply styling, and configure behaviors more efficiently. 'updateProperties' automatically tracks Observables and can otherwise be manually triggered by calling 'setNeedsUpdateProperties'. By using this method, you can avoid unnecessary layout passes, improving performance.
- 15:45 - Improvements to animations
In iOS 26, UIKit introduces 'flushUpdates', an animation option that automatically applies pending updates before and after animations, eliminating the need for manual 'layoutIfNeeded' calls. This simplifies code, reduces errors, and works with Observable objects and auto layout constraint changes.
- 17:45 - Scene updates
You can now integrate SwiftUI scenes into UIKit apps using the new 'UIHostingSceneDelegate' protocol. Create apps that adapt to different devices — like a meditation app with a 2D zen garden on iPhone and iPad and an immersive visionOS experience — by programmatically requesting a specific SwiftUI scene.
- 18:55 - HDR Color support
In iOS 26, UIKit also enhances HDR rendering beyond images to include colors. Create HDR colors using 'UIColor' and enable HDR color selection in color pickers. Use the new 'UITraitHDRHeadroomUsage' trait to monitor when your HDR content should fall back to SDR.
- 20:38 - Swift notifications
UIKit represents each notification as a dedicated 'NotificationCenter.Message' type in iOS 26, providing strongly typed notifications for easier event handling, as seen in an example of adjusting layout when the keyboard appears.
- 21:20 - Migrate to a scene-based life cycle
'UIScene' replaces 'UIApplication' as the standard for app development, making apps more portable and flexible. You must adopt the 'UIScene' life cycle because legacy methods are deprecated and, starting with the release following iOS 26, apps that have not adopted the scene life cycle will not launch.
- 22:40 - OpenURL support for file URLs
The 'openURL' method now allows apps to hand off non-native documents to default viewers or use quick look preview controllers if no default exists.
- 23:17 - SF Symbols 7
SF Symbols 7 introduces new drawing capabilities including 'Draw Off' and 'Draw On' effects, variable draw mode for drawing arbitrary values along a path, and special draw animations with Magic Replace transitions. UIKit now has API for easy adoption of these transitions in 'UIButton', and symbols can be colored with automatically-generated gradients.
- 25:13 - Next steps
To update your app for iOS 26, compile with the new SDK, refine UIs to match the new design, utilize standard containers and new menu APIs, and adopt the 'updateProperties' method and observation tracking for improved performance.