一、前言

  • 阅读了我的前两篇博客的朋友,应该都熟练掌握了 SwiftUI 如何创建一个任何相关信息的展示视图和各个视图之间的相互组合,以及动态生成一个展示相关信息的可滚动列表,用户可以点击列表项去查看其相关的详细信息。那么,当我们需要去标记相关的信息,亦或者过滤信息列表,只需要展示必要的相关信息,该怎么继续处理呢?
    • 有关详情视图的创建和视图的组合的示例,请参考我的博客:SwiftUI之深入解析如何创建和组合视图;
    • 动态生成一个展示相关信息的可滚动列表,可以点击查看其相关的详细信息的示例,请参考我的博客:SwiftUI之深入解析如何创建列表展示视图和列表如何导航跳转新页面。
  • 要增加这些特性,就比如以地图上的地理位置和地点为例,我们需要在地点列表上添加一个开关,用来过滤用户喜欢的地标,还需要在地标上添加一个星标按钮,用户可以点击它来标记这个地标为自己喜欢的地点。

二、标记特定的数据

  • 标记用户最喜欢的地标,给地标列表的每一行添加一个星标用来表示用户是否标记该地标为自己喜欢的:

  • 打开工程项目,在项目导航下选择 LandmarkRow.swift 文件,在空白占位后面添加一个 if 表达式,if 表达式判断是否当前地标是用户喜欢的,如果用户标记当前地标为喜欢就显示星标,可以在 SwitUI 的代码块中使用 if 语句来条件包含视图;由于系统图片是矢量类型的,可以使用 foregroundColor( _: ) 来改变它的颜色,当地标 landmark 的 isFavorite 属性为真时,星标显示;
struct LandmarkRow: View {var landmark : Landmarkvar body: some View {HStack {landmark.image.resizable().frame(width: 50, height: 50)Text(landmark.name)Spacer()if (landmark.isFavorite) {Image(systemName: "star.fill").imageScale(.medium).foregroundColor(.yellow)}}}
}struct LandmarkRow_Previews: PreviewProvider {static var previews: some View {Group {LandmarkRow(landmark: landmarkData[0])LandmarkRow(landmark: landmarkData[1])}.previewLayout(.fixed(width: 300, height: 70))}
}

三、过滤数据列表

  • 可以定制地标列表,让它只显示用户喜欢的地标,或者显示所有的地标,要实现这个功能,需要给 LandmarkList 视图类型添加一些状态变量,状态(State)是一个值或者一个值的集合,会随着时间而改变,同时会影响视图的内容、行为或布局,在属性前面加上 @State 修饰词就是给视图添加了一个状态值:

  • 选择 LandmarkList.swift 文件,并给 LandmarkList 添加一个名为 showFavoritesOnly 的状态,初始值设置为 false;点击 Resume 按钮或快捷键 Command+Option+P 刷新画布,当对视图进行添加或修改属性等结构性改变时,需要手动刷新画布;
  • 代码中通过检查 showFavoritesOnly 属性和每一个地标的 isFavorite 属性值来过滤地标列表所展示的内容:
struct LandmarkList: View {@State var showFavoritesOnly : Bool = falsevar body: some View {NavigationView {List(landmarkData) { landmark inif !self.showFavoritesOnly || landmark.isFavorite {NavigationLink(destination: ContentView(landmark: landmark)) {LandmarkRow(landmark: landmark)}}}.navigationBarTitle(Text("Landmarks"))}}
}

四、添加控件来切换状态

  • 为了让用户控制地标列表的过滤器,需要添加一个可以修改 showFavoritesOnly 值的控件,传递一个绑定关系给 toggle 控件可以实现;一个绑定关系(binding)是对可变状态的引用,当用户点击 toggle 控件,从开到关或从关到开,toggle 控件会通过绑定关系对应的更新视图的状态:

  • 创建一个嵌套的 ForEach 组来把地标数据转换成地标行视图,在一个列表中组合静态和动态视图,或者组合两个甚至多个不同的动态视图组,使用 ForEach 类型动态生成而不是给列表传入数据集合生成列表视图;
  • 添加一个 Toggle 视图作为列表的每一个子视图,传入一个 showFavoritesOnly 的绑定关系,使用 $ 前缀来获得一个状态变量或属性的绑定关系;
struct LandmarkList: View {@State var showFavoritesOnly : Bool = falsevar body: some View {NavigationView {List {Toggle(isOn: $showFavoritesOnly) {Text("Favorites only")}ForEach(landmarkData) { landmark inif !self.showFavoritesOnly || landmark.isFavorite {NavigationLink(destination: ContentView(landmark: landmark)) {LandmarkRow(landmark: landmark)}}}}.navigationBarTitle(Text("Landmarks"))}}
}
  • 效果如下:

  • 实时预览模式下,点击 Toggle 控件来验证过滤器的功能:

五、使用可观察对象来存储数据

  • 要实现用户标记哪个地标为自己喜爱的地标这个功能,需要使用可观察对象(observalble object)存放地标数据。可观察对象是一种可以绑定到具体 SwifUI 视图环境中的数据对象,SwiftUI 可以察觉它影响视图展示的任何变化,并在这种变化发生后及时更新对应视图的展示内容:

  • 创建一个名为 UserData.swift 的文件,声明一个遵循 ObservableObject 协议的新数据模型,ObservableObject 协议来自响应式框架 Combine,SwiftUI 可以订阅可观察对象,并在数据发生改变时更新视图的显示内容;
  • 添加存储属性 showFavoritesOnly 和 landmarks,并赋予初始值,可观察对象需要对外公布内部数据的任何改动,因此订阅此可观察对象的订阅者就可以获得对应的数据改动信息;
  • 给新建的数据模型的每一个属性添加 @Published 属性修饰词:
import Combine
import SwiftUIfinal class UserData: ObservableObject {@Published var showFavoritesOnly = false@Published var landmarks = landmarkData
}

六、视图中适配数据模型对象

  • 已经创建 UserData 可观察对象,现在要改造视图,让它使用这个新的数据模型来存储视图内容数据:

  • 在 LandmarkList.swift 文件中,使用 @EnvironmentObject 修饰的 userData 属性来替换原来的 showFavoritesOnly 状态属性,并对预览视图调用 environmentObject( _: ) 修改器,只要 environmentObject( _: ) 修改器应用在视图的父视图上,userData 就能够自动获取它的值;
  • 替换原来使用 showFavoritesOnly 状态属性的地方,改为使用 userData 中的对应属性,与 @State 修饰的属性一样,也可以使用 $ 前缀访问 userData 对象的成员绑定引用;
  • 创建 ForEach 实例时使用 userData.landmarks 做为数据源:
struct LandmarkList: View {@EnvironmentObject private var userData: UserDatavar body: some View {NavigationView {List {Toggle(isOn: $userData.showFavoritesOnly) {Text("Show Favorites Only")}ForEach(userData.landmarks) { landmark inif !self.userData.showFavoritesOnly || landmark.isFavorite {NavigationLink(destination: LandmarkDetail(landmark: landmark).environmentObject(self.userData)) {LandmarkRow(landmark: landmark)}}}}.navigationBarTitle(Text("Landmarks"))}}
}struct LandmarksList_Previews: PreviewProvider {static var previews: some View {LandmarkList().environmentObject(UserData())}
}

  • 在程序入口,对 LandmarkList 视图调用 environmentObject 修改器,这样可以把 UserData 的数据对象绑定到 LandmarkList 视图的环境变量中,子视图可以获得父视图环境中的变量,此时如果在模拟器或者真机上运行应用,也可以正常展示视图内容:
@main
struct HandlingUserInputApp: App {var body: some Scene {WindowGroup {LandmarkList().environmentObject(UserData())}}
}
  • 更新 ContentView 视图,让它从父视图的环境变量中取要展示的数据,之后在更新地标的用户喜爱状态时,会用到 landmarkIndex 这个变量:
struct ContentView: View {@EnvironmentObject var userData: UserDatavar landmark: Landmarkvar landmarkIndex: Int {userData.landmarks.firstIndex(where: { $0.id == landmark.id })!}var body: some View {VStack {MapView(coordinate: landmark.locationCoordinate).edgesIgnoringSafeArea(.top).frame(height: 300)CircleImage(image: landmark.image).offset(x: 0, y: -130).padding(.bottom, -130)VStack(alignment: .leading) {HStack {Text(landmark.name).font(.title)}HStack(alignment: .top) {Text(landmark.park).font(.subheadline)Spacer()Text(landmark.state).font(.subheadline)}}.padding()Spacer()}}
}struct ContentView_Previews: PreviewProvider {static var previews: some View {let userData = UserData()return ContentView(landmark: landmarkData[0]).environmentObject(userData)}
}
  • 切换到 LandmarkList.swift 文件,并打开实时预览视图去验证所添加的功能是否正常工作。

七、为每一个地标创建一个喜爱按钮

  • 可以在喜欢和不喜欢的地标列表间进行切换,但喜欢的地标列表还是硬编码形成的,为了让用户可以自己标记哪个地标是自己喜欢的,需要在地标详情页添加一个标记喜欢的按钮:

  • 在 ContentView.swift 的 HStack 中添加地标名称的 Text,在地标名称的 Text 控件旁边添加一个新的按钮控件,使用 if-else 条件语句设置不同的图片显示状态表示这个地标是否被用户标记为喜欢,在 Button 的动作闭包中,使用 landmarkIndex 去修改 userData 中对应地标的数据:

  • 切换到 landmarkList.swift,并开启实时预览模式,当从列表页导航进入详情页后,点击喜欢按钮,喜欢的状态会在返回列表页后与列表中对应的地标喜欢状态保持一致,因为列表页和详情页的地标数据使用的是同一份,所以可以在不同页面间保持状态同步。
  • 完整示例:SwiftUI之如何处理特定的数据和如何在视图中适配数据模型对象。

SwiftUI之深入解析如何处理特定的数据和如何在视图中适配数据模型对象相关推荐

  1. vue中什么样的数据可以是在视图中显示

    1. Vue中不可以添加不存在的属性,因为不存在的属性是没有getter和setter的. <div id="app">{{msg.a}}{{msg.b}}</d ...

  2. java对象复制到另一个对象中_spring: beanutils.copyproperties将一个对象的数据塞入到另一个对象中(合并对象)...

    spring: beanutils.copyproperties将一个对象的数据塞入到另一个对象中(合并对象) 它的出现原因: BeanUtils提供对Java反射和自省API的包装.其主要目的是利用 ...

  3. 超简单,一行代码解决:如何快速将Excel表格数据,映射到Java中的ListVO对象,然后转存数据库,生成SQL脚本

    前言-真的很快速,之前用的是Apache下的Jar包,用起来太麻烦了 <dependency><groupId>org.apache.poi</groupId>&l ...

  4. SwiftUI之深入解析@StateObject、@ObservedObject和@EnvironmentObject的联系和区别

    一.@State 属性包装器 ① 什么是 @State 属性包装器? 状态在任何现代应用程序中都是不可避免的,但在 SwiftUI 中,重要的是所有的视图都是它们状态的简单函数,我们不需要直接改变视图 ...

  5. SpringMVC中使用作用域对象完成数据的流转

    文章目录 SpringMVC中使用作用域对象完成数据的流转 [1] 作用域对象复习 [2] SpringMVC中使用作用域对象流转数据 [3] SpringMVC的Model对象的使用 SpringM ...

  6. 如何在使用ASPMVC4的分部视图中获取数据展示

    如何在使用ASPMVC4的分部视图中获取数据展示 在ASPMVC4中,创建的网站项目会用到分部视图,通过@Html.Partial("视图名")来加载到页面上: 但是如何把数据附加 ...

  7. SwiftUI之深入解析属性包装器如何处理结构体

    已经了解了 SwiftUI 如何通过使用 @State 属性包装器将变化的数据存储在结构体中,如何使用 $ 将状态绑定到 UI 控件的值,以及更改 @state 包装的属性时是如何自动让 SwiftU ...

  8. 解析BW:数据源提取数据的原理

    解析BW:数据源提取数据的原理 题记:忽然想到这么个问题,后勤数据源和非后勤数据初始化有何区别,然后进行周边的拓展,所以就形成了下文.大部分知识源于TBW350和SAP SDN. 对数据源抽取机制的深 ...

  9. SwiftUI之深入解析高级动画的几何效果GeometryEffect

    一.前言 在我的博客 SwiftUI之深入解析高级动画的路径Paths 中,已经了解了 Animatable 的协议,以及如何使用它来动画路径.接下来,我们将使用相同的协议来动画变换矩阵,使用一个新的 ...

最新文章

  1. opencv cv2.flip()函数(图像翻转、镜像、颠倒)(沿x轴翻转0、沿y轴翻转【正值】、同时沿x轴y轴翻转-【负值】)
  2. Flex4_HttpService组件
  3. .NET6之MiniAPI(二十四):用Polly重试
  4. 产品经理常犯的七大错误
  5. linux 内核字符驱动char_dev源码分析
  6. Unity5 Sprite 图集打包 AssetBundle 更新探索
  7. 回望2017:一个前端从业者砥砺前行的一年
  8. sql unicode转中文_汉字转拼音的Java类库 JPinyin
  9. 接口测试——Fiddler使用要点——笔记整理
  10. 为什么很少人学汇编_要想精通C语言,必须先学习汇编吗
  11. Assigning retained object to weak property object will be released after assignment
  12. 20200203每日一句
  13. 拼命成为有能力为自己老年生活买单的人|独秀日记
  14. 【ClearCase使用】之图解merge
  15. 动物基因组测序基础分析流程总结(GWAS全流程第一部分:WGS基础流程)
  16. 路由器下一跳地址怎么判断_Tracert命令详解,路由跟踪命令tracert命令怎么用?...
  17. mp3外链,文件上传,QQ空间永久背景音乐,mp3连接,
  18. 第一只python小爬虫
  19. 实例:时间事件日志分析
  20. 文本分析 | 管理层讨论信息含量原理与代码实现

热门文章

  1. 使用 case when进行行列转换
  2. CentOS7,linux下nginx的安装过程——1.安装pcre与nginx——源码
  3. JavaWeb第二讲 重定向与转发 doGet()与doPost()
  4. 你可能不知道的小知识-bug为什么叫bug
  5. (二十九)、Java字符串中去除空格
  6. Linux C编程之四 动态库(共享库)的制作
  7. h5页面长按保存图片
  8. HDU - 3336 next运用+递推
  9. 51nod 1277 KMP 前缀出现次数
  10. “进度条”博客——第四周