SwiftUI 核心要素 处理用户输入 在 Landmarks 应用中,用户可以标记他们喜欢的地方,并筛选列表以仅显示他们的喜欢项。要创建此功能,您将首先在列表中添加一个开关,以便用户专注于仅他们的喜欢项,然后添加一个星形按钮,用户可以点击该按钮将地标标记为喜欢项。
下载启动项目并跟随本教程一起操作,或者打开完成的项目并自行探索代码。
第 1 节
标记 favorite landmarks 首先增强列表,以便人们一眼就能看到他们的喜欢项。向 Landmark 结构添加一个属性以读取地标最初是否为喜欢项的状态,然后向每个 LandmarkRow 添加一个星形图标,以显示喜欢的地标。
步骤 1
打开起始点 Xcode 项目或上一教程完成的项目,并在项目导航器中选择 Landmark 。
第 2 步
在 Landmark 结构中添加一个 isFavorite 属性。
landmarkData 文件中的每个地标都有一个带有此名称的键。由于 Landmark 遵循 Codable ,您可以创建一个具有与键相同名称的新属性来读取与键关联的值。
第 3 步
在项目导航器中选择 LandmarkRow 。
第 4 步
在空格后,添加一个星形图像到 if 语句中以测试当前地标是否为收藏。
在 SwiftUI 代码块中,您使用 if 语句来有条件地包含视图。
第 5 步
因为系统图像基于向量,您可以使用 foregroundStyle(_😃 修饰符更改其颜色。
当地标 isFavorite 属性为 true 时,星形图像会显示。您将在本教程稍后部分看到如何修改该属性。
第 2 节
过滤列表视图 您可以自定义列表视图,使其显示所有地标,或者仅显示用户的收藏。要实现这一点,您需要向 LandmarkList 类型添加一些状态。
状态是在时间上可以变化的一个或一组值,它影响视图的行为、内容或布局。您使用具有 @State 属性的属性来向视图添加状态。
步骤 1
在项目导航器中选择 LandmarkList 。
第 2 步
添加一个名为 @State 的属性,并将其初始值设置为 false 。
因为你使用状态属性来保存特定于视图及其子视图的信息,所以你总是将状态创建为 private 。
第 3 步
当你更改视图的结构,例如添加或修改属性时,画布会自动刷新。
提示 如果画布不可见,请选择编辑 > 画布以显示它。
第 4 步
通过检查 showFavoritesOnly 属性和每个 landmark.isFavorite 值,计算地标列表的过滤版本。
第 5 步
在 List 中使用地标列表的过滤版本。
第 6 步
将 showFavoritesOnly 的初始值更改为 true ,以查看列表的反应。
第三部分
添加一个控件以切换状态 为了给用户控制列表的过滤器,你需要添加一个可以改变 showFavoritesOnly 值的控件。你通过传递一个绑定到一个切换控件来实现这一点。
绑定充当可变状态的引用。当用户从关闭切换到开启,然后再切换回关闭时,控件使用绑定来相应地更新视图的状态。
步骤 1
创建一个嵌套的 ForEach 组以将地标转换为行。
要在一个列表中结合静态和动态视图,或者结合两个或多个不同组的动态视图,请使用 ForEach 类型,而不是将你的数据集合传递给 List 。
第 2 步
将一个 Toggle 视图作为第一个子视图添加到 List 视图中,并传递一个绑定到 showFavoritesOnly 的绑定。
使用 $ 前缀来访问一个状态变量的绑定,或其某个属性的绑定。
第 3 步
通过添加一个 animation(_😃 修饰符来改进过滤动画,该修饰符在 filteredLandmarks 值发生变化时开始。
第 4 步
在继续之前,将 showsFavoritesOnly 的默认值返回到 false 。
第 5 步
使用实时预览并点击切换按钮尝试这项新功能。
第 4 节
使用观察进行存储 为了准备让用户控制哪些特定地标是收藏项,你将首先使用 Observable() 宏存储地标数据。
使用 Observation,SwiftUI 视图可以在不使用属性包装器或绑定的情况下支持数据变化。SwiftUI 会监视任何可能影响视图的可观察属性变化,并在更改后显示正确的视图版本。
步骤 1
在项目的导航栏中,选择 ModelData 。
第 2 步
使用 Observable() 宏声明一个新的模型类型。
SwiftUI 只会在可观察属性发生变化且视图的主体直接读取该属性时更新视图。
第 3 步
将 landmarks 数组移动到模型中。
提示 您可以使用 Command + Option + { 或 } 分别将选中的代码行向上和向下移动。
第 5 节
在你的视图中采用模型对象 现在你已经创建了 ModelData 对象,需要更新视图以采用它作为应用程序的数据存储。
步骤 1
在 LandmarkList 中,向视图添加一个 @Environment 属性包装器,并向预览添加一个 environment(_😃 修饰符。
modelData 属性会自动获取其值,只要 environment(😃 修饰符已应用于父级。 @Environment 属性包装器使你可以读取当前视图的模型数据。添加 environment(😃 修饰符会将数据对象传递给环境中的各个层级。
第 2 步
使用 modelData.landmarks 作为筛选地标的数据。
第 3 步
更新 LandmarkDetail 视图以与环境中的 ModelData 对象配合使用。
第 4 步
更新 LandmarkRow 预览以与 ModelData 对象配合使用。
第 5 步
更新 ContentView 预览以将模型对象添加到环境中,从而使该对象可供任何子视图使用。
预览失败,如果任何子视图需要环境中的模型对象,但您正在预览的视图没有 environment(_😃 修饰符。
接下来,您将更新应用程序实例,在模拟器或设备上运行应用程序时将模型对象放入环境中。
第 6 步
更新 LandmarksApp 以创建一个模型实例并使用 environment(_😃 修饰符将其传递给 ContentView 。
使用 @State 属性以与在视图内部初始化属性相同的方式初始化模型对象。就像 SwiftUI 在视图的生命周期中仅初始化一次状态一样,它在应用程序的生命周期中仅初始化一次状态。
第 7 步
切换回 LandmarkList 以验证一切是否正常工作。
第 6 节
为每个地标创建一个收藏按钮 现在,地标应用可以切换到过滤和未过滤的地标视图,但收藏的地标列表仍然硬编码。为了允许用户添加和删除收藏,您需要在地标详情视图中添加一个收藏按钮。
你将首先创建一个可重用的 FavoriteButton 。
步骤 1
创建一个名为 FavoriteButton.swift 的新视图。
第 2 步
添加一个 isSet 绑定,以指示按钮的当前状态,并为预览提供一个常量值。
绑定属性包装器使您能够读取和写入存储数据的属性和显示并更改数据的视图之间。由于使用了绑定,此视图中的更改会传播回数据源。
第 3 步
创建一个带有切换 isSet 状态的动作的 Button ,并且根据状态更改其外观。
你为按钮标签提供的标题字符串在使用 iconOnly 标签样式时不会在 UI 中出现,但 VoiceOver 会使用它以提高无障碍性。
随着项目的增长,添加层级是个好主意。在继续之前,请创建几个更多的组。
第 4 步
将通用的 CircleImage 、 MapView 和 FavoriteButton 文件收集到一个 Helpers 组中,并将地标视图收集到一个 Landmarks 组中。
接下来,你将向详细视图添加 FavoriteButton ,将按钮的 isSet 属性绑定到给定地标上的 isFavorite 属性。
第 5 步
切换到 LandmarkDetail ,并通过与模型数据进行比较来计算输入地标的位置索引。
为此,您还需要访问环境的模型数据。
第 6 步
在 body 属性中,使用 Bindable 包裹添加模型数据。将地标名称嵌入 HStack 中,并使用新的 FavoriteButton ;将绑定设置为 isFavorite 属性,使用美元符号( $ )。
使用 landmarkIndex 和 modelData 对象确保按钮更新模型对象中存储的地标 isFavorite 属性。
第 7 步
切换回 LandmarkList ,并确保启用了实时预览。
从列表导航到详细信息并点击按钮时,返回列表时这些更改会保持不变。因为两个视图访问的是环境中的同一个模型对象,所以两个视图保持一致。