大多数浏览器和
Developer App 均支持流媒体播放。
-
将 SwiftUI 与 UIKit 搭配使用
了解如何在您的 UIKit App 中利用 SwiftUI 的功能。使用 UIHostingConfiguration 来构建与 SwiftUI 无缝兼容的自定义 UICollectionView 和 UITableView 单元格。我们还将向您介绍如何在您的 App 中管理 UIKit 和 SwiftUI 之间的数据流。 为能更好地理解此讲座,我们建议您先对 SwiftUI 进行基本了解。
资源
- Managing model data in your app
- selfSizingInvalidation
- selfSizingInvalidation
- UIHostingConfiguration
- UIHostingController
- UIViewController
- Using SwiftUI with UIKit
相关视频
WWDC22
WWDC20
-
下载
♪ 柔和乐器演奏的嘻哈音乐 ♪ ♪ 大家好 我是 Sara Frederixon Health App 的工程师 我将和您讨论如何将 SwiftUI 与 UIKit 结合使用 像您们中的许多人一样 我的工作是基于现有的UIKit 开发 App 对我来说,这个App就是 Health App Health App 有许多可视化信息 以帮助人们了解自己的健康数据 但是构建这些视图可能非常复杂 我一直对 使用 SwiftUI 带来的优势很感兴趣 所以我与 UIKit 和 SwiftUI 团队一起工作 以了解如何 将两者集成到同一个 App 中 在这个视频中 我将为您展示 在您自己的 UIKit App 中 使用 SwiftUI 多么简单 首先 我将介绍现有 UIHostingController 它包含一些更新内容 灵活性也更强 接下来我将深入讲解用您的 App 中的现有 数据填充 SwiftUI 视图 以及如何确保在数据出现变更时 及时更新 SwiftUI 视图 然后 我将介绍一些 令人兴奋的新功能 它们可以让您用 SwiftUI 构建 UICollectionView 和 UITableView 单元格 最后 我将介绍数据流 有哪些独特的方面 当您在 collection 和 table view 单元格内 使用 SwiftUI 视图 我们先来讨论一下 UIHostingController UIHostingController 是一个 UIViewController 它包含 SwiftUI 视图层次结构 能在 UIKit 中 使用视图控制器的地方 就可以使用托管控制器 这让 UIHostingController 成为了开始使用 SwiftUI 的简便方法 让我们来看看 托管控制器是如何工作的 托管控制器是一个视图控制器 这意味着它的视图属性中 存储了一个 UIView 那个视图里面就是 绘制 SwiftUI 内容的地方 让我们举例说明如何使用托管控制器 在这里 我们创建一个 HeartRateView 一个 SwiftUI 视图 然后 我们创建一个托管控制器 将 HeartRateView 作为它的根视图 并呈现出来 UIHostingController 可与所有 UIKit 视图控制器 API 搭配使用 让我们来看看另一个例子 我们有相同的 HeartRateView 以及和以前一样的托管控制器 在这里 我们添加托管控制器 将其作为子视图控制器 然后 我们可以调整 托管控制器的视图位置和大小 当 UIHostingController 中的 SwiftUI 内容发生变化时 您需要调整视图的大小 作为 iOS 16 中的新功能 UIHostingController 允许您启用 视图控制器首选内容大小 和视图固有内容大小的自动更新 您可以使用 UIHostingController 上的 新 sizingOptions 属性 启用此功能 我们举个例子 首先 我们制作 HeartRateView 并创建 hostingController 我们使用新 sizingOptions API 让托管控制器 自动更新其 preferredContentSize 然后 我们进行设置 让 modalPresentationStyle 弹出 使用新 sizingOptions API 可确保 弹出框的大小适合 SwiftUI 内容 现在您已经熟悉了 UIHostingController 我们来谈谈 如何将 UIKit App 的 其他部分中的数据 导入 SwiftUI 并确保当该数据发生变更时 您的 SwiftUI 视图 能及时更新 这是您 UIKit App 的图表 它包含一个现有的模型层 它拥有并管理 App 的数据模型对象 您的 App 还包含 许多视图控制器 如果您想开始使用 SwiftUI 您需要一个托管控制器 其中一个视图控制器中 有 SwiftUI 视图 您将用仍然您现有模型层所有的数据 填充此 SwiftUI 视图 在本部分 我们将重点介绍如何 跨越 UIKit 和 SwiftUI 的界限桥接数据 SwiftUI 提供了多种数据流原语 以帮助您管理 App 中的数据 让我们来看看不同的选项 要存储由 SwiftUI 视图 创建和拥有的数据 SwiftUI 提供了 @State 和 @StateObject 属性包装器 由于我们专注于 SwiftUI 之外的数据 这些属性包装器并不合适 所以 我不会在 本视频中介绍这些内容 观看“SwiftUI 中的数据要点” 以了解有关 SwiftUI 视图 拥有的数据的更多信息 处理 SwiftUI 外部数据的一种方法 是在初始化视图时直接传递数值 因为只是传递并非由 SwiftUI 拥有或管理的原始数据 所以您负责在数据发生变化时 手动更新 UIHostingController 我们举个例子 这是一个名为 HeartRateView 的 SwiftUI 视图 这个视图只有一个属性 以整数形式 存储的每分钟的 beatsPerMinute 即心率 并将此数值显示为文本 我们通过在现有的名为 HeartRateViewController 的 视图控制器中 嵌入一个 UIHostingController 来显示这个 HeartRateView 我们保存对托管控制器的引用 以便稍后更新它的根视图 请记住 SwiftUI HeartRateView 是一个值类型 所以单独存储它 会创建一个单独的副本 让我们不能更新 UI HeartRateViewController 拥有 用于填充 HeartRateView 数据 此数据存储在 beatsPerMinute 属性中 当 beatsPerMinute 值发生变化时 我们将调用一个方法来更新视图 在更新方法中 我们使用 最新的 beatsPerMinute 值 创建一个新的 HeartRateView 然后将该视图指定为 托管控制器的 rootView 这是将数据从 UIKit 导入 SwiftUI 的简单方法 但您需要在数据发生变化时 及时手动更新托管控制器的 rootView 让我们看看 其他 SwiftUI 数据原语 以实现数据自动更新 @ObservedObject 和 @EnvironmentObject 属性包装器允许您引用 ObservableObject 协议的 外部模型对象 当您使用这些属性包装器时 SwiftUI 会在数据更改时 自动更新您的视图 在此视频中 我们将重点介绍 @ObservedObject 属性包装器 您可以在前面提到的 “SwiftUI 中的数据要”点视频中 了解 EnvironmentObject 的更多内容 让我们来看看 如何创建 @ObservedObject 第一步是取一个 App 的现有部分 拥有的模型对象 并使其符合 ObservableObject 协议 接下来 我们在我 SwiftUI 视图中 将模型存储为 @ObservedObject 属性 将 ObservableObject 连接到 SwiftUI 使它能够 在其中一个属性更改时更新视图 回顾一下 HeartRateView 示例 并将其连接起来 我们的 App 有一个 名为 HeartData 的类别 其中包含每分钟 beatsPerMinute 属性 我们按照协议使其成为 ObservableObject 然后我们将 @Published 属性包装器 添加到 beatsPerMinute 属性中 此属性包装器会在发生变化时 触发 SwiftUI 更新我们的视图 在 HeartRateView 里 我们将 HeartData 存储在 标有 @ObservedObject 属性包装器的属性中 在视图的主体部分 我们显示直接来源于 HeartData 的 beatsPerMinute 现在 让我们 在视图控制器中使用一下所有功能 这是我们的 HeartRateViewController 它将 HeartData ObservableObject 存储在一个属性中 因为这个属性 不在 SwiftUI 视图中 所以我们不需要 在这里使用属性包装器 HeartRateViewController 已用 HeartData 实例 进行了初始化 该数据用于 创建 HeartRateView 此视图将成为托管控制器的 rootView 该图说明了它们是如何结合在一起的 我们获取 当前的 HeartData 实例 其中包含心率每分钟 78 次 然后我们 用此 HeartData 实例 创建一个 新 HeartRateViewController 并将 HeartData 连接到 SwiftUI HeartRateView 几秒钟后 即当下一个心率数据样本到达时 HeartData 的 beatsPerMinute 属性更新为 94 因为这更改了 ObservableObject 上的 published 属性 HeartRateView 会自动更新以显示新值 所以我们不再需要在数据发生变更时 手动更新托管控制器 这就是为什么 ObservableObject 是桥接从 UIKit 到 SwiftUI 的数据的好方法 接下来说一下在集合视图以及 表格视图单元格中使用 SwiftUI iOS 16 中的新功能是 UIHostingConfiguration 它可以让您在您现有的 UIKit、集合和表格视图中 使用 SwiftUI 的强大功能 UIHostingConfiguration 让您可以轻松地 使用 SwiftUI 实现自定义单元格 同时您不必担心嵌入 额外的视图或视图控制器 在我们深入了解 UIHostingConfiguration 之前 让我们介绍一下 UIKit 中的单元格配置 单元格配置是一种 在 UIKit 中定义 单元格的内容、样式 和行为的现代方式 与 UIView 或 UIViewController 不同 配置只是一个轻量级结构 创建成本不高 配置只是描述单元格的外观 因此需要应用 到单元格才有效果 配置是可组合的 并可用于 UICollectionView 和 UITableView 单元格 要了解更多详情 您可以观看“现代单元格配置” 有了这些知识 我们将开始探究并使用 UIHostingConfiguration UIHostingConfiguration 是一个内容配置 它使用 SwiftUI ViewBuilder 进行初始化 这意味着我们可以 开始编写 SwiftUI 代码 以直接在其中创建视图 为了渲染托管配置 我们将其设置为 UICollectionView 或 UITableView 单元格中的 contentConfiguration 属性 让我们开始在此托管配置中 编写一些 SwiftUI 代码 以构建自定义心率单元格 首先 我们将创建一个 label 带有“Heart Rate”文本 和一颗心形图像 SwiftUI 视图根据其使用环境 接收默认样式 但我们可以使用标准的 SwiftUI 视图修饰符自定义样式 我们将 foregroundStyle 和 font 修饰符添加到标签中 以将图像和文本设置为粉红色并加粗 因为我们只是在 编写常规的 SwiftUI 代码 我们可以根据需要随时 将代码提出为一个独立的视图 在这里 我们创建一个 新的 SwiftUI 视图 命名为 HeartRateTitleView 并将前面的代码移动到其 body 中 然后在托管配置中嵌入 HeartRateTitleView 如单元格所示 结果是完全相同的 现在我们可以在 HeartRateTitleView 中 开始添加更多视图 我将 label 和 spacer 放在 HStack 里面 然后在旁边的 Text 视图中添加当前时间 到目前为止看起来还不错 我们来向 HeartRateTitleView 下方的 自定义单元格中添加更多内容 为此 我们将在托管配置中 插入一个 Vstack 堆栈 这样我们就可以在 HeartRateTitleView 下添加更多内容 然后 我们将两个 Text 视图一起 放在 HStack 中 以显示 90 BPM 然后 应用一些修饰符 以按照需要设置样式 就像我们之前对 HeartRateTitleView 所做的那样 我们可以将这个新代码移动到 其自己的 SwiftUI 视图中 现在 相同的代码被提取到了 HeartRateBPMView 的 body 中 我们的单元格看起来很棒 不过我觉得我们还可以添加一个东西 iOS 16 中有一个新功能是 Swift Charts 框架 只需编写几行代码 您就可以用漂亮的图表来显示数据 让我们试着用它在单元格内 显示一个小的折线图 我们使用新的 Chart 视图 创建一个小折线图 以显示最近的心率样本 并把它放在 单元格中的 BPM 视图旁边 要生成图表 我们需要传入一组心率样本 并绘制一个连接所有样本的 LineMark 我们可以添加圆圈符号 以表示折线上的每个样本 并应用粉色的前景样式 以为图表着色 从而匹配 HeartRateTitleView 我们仅仅介绍了 新 Swift Charts 框架的几个功能 请务必观看 “Hello Swift Charts”视频 以了解更多信息 我们制作的心率单元格很棒 而且只需要几分钟就可以完成 用 UIHostingConfiguration 和 SwiftUI 构建自定义单元格就是这么简单 我们来聊一聊 UIHostingConfiguration 支持的四大特别功能 默认情况下 根级 SwiftUI 内容 和单元格边缘有一定间距 是基于 UIKit 中 单元格的布局边距决定的 这确保了单元格内容 与相邻单元格的内容和导航栏等 其他 UI 元素正确对齐 有时 您可能想使用不同的边距 或让内容延伸到单元格的边缘 对于这些情况 您可以在 UIHostingConfiguration 上 使用边距修饰符以更改默认边距 如果您想 用 SwiftUI 自定义单元格的背景外观 您可以使用 UIHostingConfiguration 上的 background 修饰符 UIHostingConfiguration 的 背景及其内容 之间有几个关键区别 背景位于单元格的背面 即单元格内容视图中的 SwiftUI 内容下方 此外 虽然内容通常是 从单元格边缘插入的 但背景通常会从 单元格的一边拓展到另一边 最后 当使用自定尺寸单元格时 只有单元格的内容 会影响单元格的大小 接下来 让我们看看 UIHostingConfiguration 的另外两个特殊功能 您可以将其用于集合视图列表 或表格视图中的单元格 在列表中 托管配置默认将 单元格下方的间隔符 自动与 SwiftUI 文本对齐 在本例中 请注意间隔符的前缘 是如何跳过图像 以与单元格中的文本对齐 如果需要使间隔符与托管配置中 的不同 SwiftUI 视图对齐 请使用 alignmentGuide 修饰符 在集合视图列表或表格视图中 您可以直接使用 SwiftUI 为一行配置滑动操作 通过在 swipeActions 修饰符中创建按钮 您将能够滑动单元格 以显示和执行自定义操作 下载此视频的示例代码 以找到一个完整的例子 定义滑动操作时 确保您的按钮使用所代表的项目 的稳定标识符执行其操作 不要使用索引路径 因为它可能会 在单元格可见的情况下发生变化 从而导致滑动操作作用于错误的项目 当在单元格中使用 UIHostingConfiguration 时 请记住单元格的交互 例如点击处理 高亮显示 和选择将仍然通过 集合视图或表格视图处理 如果需要为 任何这些 UIKit 单元格状态 自定义 SwiftUI 视图 您可以在单元格的 configurationUpdateHandler 中 创建您的托管配置 并使用 SwiftUI 代码中提供的状态 只要单元格状态发生变化 configurationUpdateHandler 就会再次运行 为新状态创建一个 新 UIHostingConfiguration 并将其应用于单元格 在此例中 我们使用状态 来添加一个复选标记图像 它代表单元格被选中 现在您已经熟悉了 UIHostingConfiguration 让我们讨论一下如何管理 数据流动 从模型层传递到 UICollectionView 或者 UITableView 的单元格 其内部由 SwiftUI 填充 我们的目标是建立病症清单 在此例中 我们使用了一个 UICollectionView 但我们讨论的所有内容 都同样适用于 UITableView 让我们来看看所涉及的构件 我们的 App 有一个 MedicalCondition 模型对象集合 我们将在集合视图中显示 对于此集合中的每个项目 我们想在集合视图中创建一个单元格 以显示该病症 为此 我们将创建一个连接到 集合视图的可区分的数据源 然后 我们需要 填充一个可区分数据源快照 以及数据集合中的 MedicalCondition 模型对象的标识符 可区分数据源快照必须 包含每个 MedicalCondition 的唯一标识符 而不是 MedicalCondition 对象本身 这确保了可区分数据源可以准确跟踪 每个项目本身 并在稍后应用新快照时 计算正确的变化 通过将具有这些项目标识符的快照 应用于可区分数据源 它会自动更新集合视图 这将为每个项目创建一个新单元格 在 UIHostingConfiguration 中 使用 SwiftUI 视图 将每个单元格配置为 显示一个 MedicalCondition 现在我们正在显示使用 SwiftUI 构建的单元格 当数据发生变更时 我们需要处理 UI 更新 有两种不同类型的变化 我们需要分别单独处理 第一种是数据集合本身发生变化时 例如插入、重新排序或删除项目 这些变更通过将新快照 应用于可区分数据源来处理 可区分数据源将区分新旧快照 并对集合视图执行必要的更新 以插入、移动或删除单元格 因为数据集合本身的变化 不会影响单元格内的任何东西 您以相同的方式处理这些类型的更改 无论是使用 UIKit 还是 SwiftUI 构建单元格 我们需要处理的第二种变更 是单个模型对象的属性发生变更 这些变更通常需要 您更新现在单元格中的视图 因为可区分数据源 仅在其快照中包含项目标识符 它不知道现有项目 的属性何时发生变更 按照传统 使用 UIKit 时您需要手动 告知可变数据源这些变更 方式是重新配置 或重新加载快照中的项目 但是当在单元格中使用 SwiftUI 时 您就不再需要这么做了 通过在我们的 SwiftUI 视图的 ObservableObject 属性中 存储 ObservedObject 模型 对模型的已发布属性的更改 会自动触发 SwiftUI 刷新视图 这在模型和单元格内的 SwiftUI 视图 之间建立了直接连接 当做出更改时 单元格中的 SwiftUI 视图直接更新 无需通过可区分数据源 或 UICollectionView 进行更新 当单元格的数据发生变更时 单元格可能需要扩大或缩小 以适应新的内容 但是如果直接更新 SwiftUI 单元格内容 而无需通过 UIKit 集合视图如何知道 需要调整单元格的大小呢? UIHostingConfiguration 利用了 UIKit 中的 一项全新功能以完成这项工作 在 iOS 16 中 UICollectionView 和 UITableView 中的 自行调整大小单元格 现在也可以自行调整大小! 这是默认启用的 这样当您使用 UIHostingConfiguration 并且 SwiftUI 内容发生变更时 相应的单元格 就会视需要自动调整大小 请在 WWDC 2022 的 UIKit 中的新功能视频中 详细了解这项新功能的工作原理 您可能需要处理数据流的另一个问题 那就是将 SwiftUI 视图中的数据 发回到 App 的其他部分 ObservableObject 也可以帮您解决这个问题! 您可以为一个 ObservableObject 的已发布属性 创建一个双向绑定 不仅数据会从 ObservableObject 流入 SwiftUI SwiftUI 也可将变更写回到 模型对象的属性上 我们来举一个简单的示例 介绍一下通过使 MedicalCondition 单元格中的文本可编辑 创建双向绑定 这是我们的 ObservableObject 即 MedicalCondition 它将唯一标识符 存储在 ID 属性中 这是用于填充可区分 数据源快照的标识符 这个 published 属性 存储病症文本 这是 MedicalConditionView 它显示每个单元格内的病症文本 现在这个文本是只读的 我们要让它变成可编辑的 我们需要做的就是 将 Text 视图更改为 TextField 并通过添加 $ 符号前缀 创建 MedicalCondition 文本属性的绑定 当您在 TextField 中键入时 此绑定允许 SwiftUI 直接将更改写回到 ObservableObject 使用 SwiftUI 设置双向数据流 就是这么简单 UIHostingController 非常强大 可将 SwiftUI 内容 嵌入到 UIKit App 中 您的 SwiftUI 视图 在托管控制器的视图内渲染 并且只要可以 在 UIKit 中使用视图控制器 就可以使用托管控制器 使用 UIHostingController 时 请务必在 App 中 添加视图控制器和视图 许多 SwiftUI 功能 比如 工具栏、键盘快捷键 以及使用了 UIViewControllerRepresentable 的视图 需要连接到 UIKit 中的 视图控制器层次结构 以正确集成 所以切勿将托管控制器视图 和托管控制器本身分隔开来 为了形成对比 在将 UIHostingConfiguration 应用于单元格时 您的 SwiftUI 视图 会托管在单元格中 而没有 UIViewController UIHostingConfiguration 支持绝大多数 SwiftUI 功能 但请记住 依赖于 UIViewControllerRepresentable 的 SwiftUI 视图 不能在单元格内使用 在 UIHostingController 和 UIHostingConfiguration 的帮助下 您可以通过两种绝佳方式 将 SwiftUI 整合到 UIKit App 中 SwiftUI 可无缝集成 到现有的 UIKit App 中 使用 UIHostingController 在整个 App 中添加 SwiftUI 使用 UIHostingConfiguration 在集合和表格视图 中创建自定义单元格 并利用 ObservableObject 使数据和 UI 始终保持同步 立即将 SwiftUI 添加到您的 App 中吧! 谢谢收看! ♪
-
-
2:09 - Presenting a UIHostingController
// Presenting a UIHostingController let heartRateView = HeartRateView() // a SwiftUI view let hostingController = UIHostingController(rootView: heartRateView) // Present the hosting controller modally self.present(hostingController, animated: true)
-
2:31 - Embedding a UIHostingController
// Embedding a UIHostingController let heartRateView = HeartRateView() // a SwiftUI view let hostingController = UIHostingController(rootView: heartRateView) // Add the hosting controller as a child view controller self.addChild(hostingController) self.view.addSubview(hostingController.view) hostingController.didMove(toParent: self) // Now position & size the hosting controller’s view as desired…
-
3:13 - Presenting UIHostingController as a popover
// Presenting UIHostingController as a popover let heartRateView = HeartRateView() // a SwiftUI view let hostingController = UIHostingController(rootView: heartRateView) // Enable automatic preferredContentSize updates on the hosting controller hostingController.sizingOptions = .preferredContentSize hostingController.modalPresentationStyle = .popover self.present(hostingController, animated: true)
-
5:27 - Passing data to SwiftUI with manual UIHostingController updates
// Passing data to SwiftUI with manual UIHostingController updates struct HeartRateView: View { var beatsPerMinute: Int var body: some View { Text("\(beatsPerMinute) BPM") } } class HeartRateViewController: UIViewController { let hostingController: UIHostingController< HeartRateView > var beatsPerMinute: Int { didSet { update() } } func update() { hostingController.rootView = HeartRateView(beatsPerMinute: beatsPerMinute) } }
-
7:51 - Passing an ObservableObject to automatically update SwiftUI views
// Passing an ObservableObject to automatically update SwiftUI views class HeartData: ObservableObject { @Published var beatsPerMinute: Int init(beatsPerMinute: Int) { self.beatsPerMinute = beatsPerMinute } } struct HeartRateView: View { @ObservedObject var data: HeartData var body: some View { Text("\(data.beatsPerMinute) BPM") } }
-
8:30 - Passing an ObservableObject to automatically update SwiftUI views
// Passing an ObservableObject to automatically update SwiftUI views class HeartRateViewController: UIViewController { let data: HeartData let hostingController: UIHostingController<HeartRateView> init(data: HeartData) { self.data = data let heartRateView = HeartRateView(data: data) self.hostingController = UIHostingController(rootView: heartRateView) } }
-
9:52 - UIHostingConfiguration
cell.contentConfiguration = UIHostingConfiguration { // Start writing SwiftUI here! }
-
11:02 - Building a custom cell using SwiftUI with UIHostingConfiguration
// Building a custom cell using SwiftUI with UIHostingConfiguration cell.contentConfiguration = UIHostingConfiguration { HeartRateTitleView() } struct HeartRateTitleView: View { var body: some View { HStack { Label("Heart Rate", systemImage: "heart.fill") .foregroundStyle(.pink) .font(.system(.subheadline, weight: .bold)) Spacer() Text(Date(), style: .time) .foregroundStyle(.secondary) .font(.footnote) } } }
-
12:46 - Building a custom cell using SwiftUI with UIHostingConfiguration
// Building a custom cell using SwiftUI with UIHostingConfiguration cell.contentConfiguration = UIHostingConfiguration { VStack(alignment: .leading) { HeartRateTitleView() Spacer() HeartRateBPMView() } } struct HeartRateBPMView: View { var body: some View { HStack(alignment: .firstTextBaseline) { Text("90") .font(.system(.title, weight: .semibold)) Text("BPM") .foregroundStyle(.secondary) .font(.system(.subheadline, weight: .bold)) } } }
-
13:41 - Building a custom cell using SwiftUI with UIHostingConfiguration, with a chart!
// Building a custom cell using SwiftUI with UIHostingConfiguration cell.contentConfiguration = UIHostingConfiguration { VStack(alignment: .leading) { HeartRateTitleView() Spacer() HStack(alignment: .bottom) { HeartRateBPMView() Spacer() Chart(heartRateSamples) { sample in LineMark(x: .value("Time", sample.time), y: .value("BPM", sample.beatsPerMinute)) .symbol(Circle().strokeBorder(lineWidth: 2)) .foregroundStyle(.pink) } } } }
-
14:41 - Content margins
cell.contentConfiguration = UIHostingConfiguration { HeartRateBPMView() } .margins(.horizontal, 16)
-
15:16 - Cell backgrounds
cell.contentConfiguration = UIHostingConfiguration { HeartTitleView() } .background(.pink)
-
16:32 - List swipe actions
cell.contentConfiguration = UIHostingConfiguration { MedicalConditionView() .swipeActions(edge: .trailing) { … } }
-
17:25 - Incorporating UIKit cell states
// Incorporating UIKit cell states cell.configurationUpdateHandler = { cell, state in cell.contentConfiguration = UIHostingConfiguration { HStack { HealthCategoryView() Spacer() if state.isSelected { Image(systemName: "checkmark") } } } }
-
23:17 - Creating a two-way binding to data in SwiftUI
// Creating a two-way binding to data in SwiftUI class MedicalCondition: Identifiable, ObservableObject { let id: UUID @Published var text: String } struct MedicalConditionView: View { @ObservedObject var condition: MedicalCondition var body: some View { HStack { Spacer() } } }
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。