SwiftUI三处理用户输入

代码下载

在Landmark应用中,标记喜爱的地方,过滤地标列表,只显示喜欢的地标。要增加这些特性,首先要在列表上添加一个开关,用来过滤用户喜欢的地标。在地标上添加一个星标按钮,用户可以点击它来标记这个地标为自己喜欢的。

在开始之前先新建项目,将之前 Model、View、Resource 目录及其中的文件复制到项目中,并将 SceneDelegate.swift、Assets.xcassets 文件替换为之前的。

标记用户最喜欢的地标

给地标列表的每一行添加一个星标用来表示用户是否标记该地标为自己喜欢的:
1、选择landmarkData.json文件,为json中的每条数据添加布尔类型的 isFavorite 字段:

[{"name": "Turtle Rock","category": "Featured","city": "Twentynine Palms","state": "California","id": 1001,"park": "Joshua Tree National Park","coordinates": {"longitude": -116.166868,"latitude": 34.011286},"imageName": "turtlerock","isFavorite": true}…

2、选择Landmark.swift文件,为Landmark结构体增加 isFavorite 属性:

    var isFavorite: Bool

3、选择LandmarkRow.swift文件,在 Spacer() 后面添加一个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).foregroundStyle(.yellow)}}}
}

过滤列表

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

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

添加控件来切换状态

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

  • 创建一个嵌套的ForEach组来把地标数据转换成地标行视图。在一个列表中组合静态和动态视图,或者组合两个甚至多个不同的动态视图组,使用ForEach类型动态生成而不是给列表传入数据集合生成列表视图
  • 添加一个Toggle视图作为列表的每一个子视图,传入一个showFavoritesOnly的绑定关系。使用$前缀来获得一个状态变量或属性的绑定关系,实时预览模式下,点击Toggle控件来验证过滤器的功能
struct LandmarkList: View {@State var showFavoritesOnly = truevar body: some View {NavigationView {List {Toggle(isOn: $showFavoritesOnly, label: {Text("Favorites only")})ForEach(Landmark.list) { landmark inif !self.showFavoritesOnly || landmark.isFavorite {NavigationLink(destination: LandmarkDetail(landmark: landmark)) {LandmarkRow(landmark: landmark)}}}}.navigationTitle(Text("Landmarks"))}}
}

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

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

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

视图中适配数据模型对象

已经创建了UserData可观察对象,现在要改造视图,让它使用这个新的数据模型来存储视图内容数据:
1、在LandmarkList.swift文件中,使用@EnvironmentObject修饰的userData属性来替换原来的showFavoritesOnly状态属性,并对预览视图调用environmentObject(:)修改器。只要environmentObject(:)修改器应用在视图的父视图上,userData就能够自动获取它的值
2、替换原来使用showFavoritesOnly状态属性的地方,改为使用userData中的对应属性。与@State修饰的属性一样,也可以使用$前缀访问userData对象的成员绑定引用
3、创建ForEach实例时使用userData.landmarks做为数据源

struct LandmarkList: View {@EnvironmentObject var userdata: UserDatavar body: some View {NavigationView {List {Toggle(isOn: $userdata.showFavoritesOnly, label: {Text("Favorites only")})ForEach(userdata.landmarks) { landmark inif !userdata.showFavoritesOnly || landmark.isFavorite {NavigationLink(destination: LandmarkDetail(landmark: landmark)) {LandmarkRow(landmark: landmark)}}}}.navigationTitle(Text("Landmarks"))}}
}#Preview {ForEach(["iPhone SE 3rd generation", "iPhone 15", "iPhone 15 Plus"], id: \.self) { deviceName inLandmarkList().previewDevice(PreviewDevice(rawValue: deviceName)).environmentObject(UserData())}
}

4、在SceneDelegate.swift中,对LandmarkList视图调用environmentObject修改器,这样可以把UserData的数据对象绑定到LandmarkList视图的环境变量中,子视图可以获得父视图环境中的变量。此时如果在模拟器或者真机上运行应用,也可以正常展示视图内容,切换到LandmarkList.swift文件,并打开实时预览视图去验证所添加的功能是否正常工作:

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).// Use a UIHostingController as window root view controllerif let windowScene = scene as? UIWindowScene {let window = UIWindow(windowScene: windowScene)window.rootViewController = UIHostingController(rootView: LandmarkList().environmentObject(UserData()))self.window = windowwindow.makeKeyAndVisible()}}

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

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

  • 更新LandmarkDetail视图,让它从父视图的环境变量中取要展示的数据。之后在更新地标的用户喜爱状态时,会用到landmarkIndex这个变量
  • 在地标名称的Text控件旁边添加一个新的按钮控件。使用if-else条件语句设置不同的图片显示状态表示这个地标是否被用户标记为喜欢。在Button的动作闭包中,使用了landmarkIndex去修改userData中对应地标的数据
struct LandmarkDetail: View {@EnvironmentObject var userdata: UserDatavar landmarksIndex: Int {userdata.landmarks.firstIndex(where: { $0.id == landmark.id }) ?? 0}var landmark: Landmarkvar body: some View {VStack {MapView(coordinate: landmark.locationCoordinate).edgesIgnoringSafeArea(.top).frame(height: 300)CircleImage(image: landmark.image).offset(y: -130).padding(.bottom, -130)VStack(alignment: .leading) {HStack {Text(landmark.name).font(.title)Button(action: {userdata.landmarks[landmarksIndex].isFavorite.toggle()}, label: {if userdata.landmarks[landmarksIndex].isFavorite {Image(systemName: "star.fill").foregroundStyle(.yellow)} else {Image(systemName: "star").foregroundStyle(.gray)}})}HStack {Text(landmark.park).font(.subheadline)Spacer()Text(landmark.state)}}.padding()Spacer()}.navigationBarTitle(Text(landmark.name), displayMode: .inline)}
}

切换到landmarkList.swift,并开启实时预览模式。当从列表页导航进入详情页后,点击喜欢按钮,喜欢的状态会在返回列表页后与列表中对应的地标喜欢状态保持一致,因为列表页和详情页的地标数据使用的是同一份,所以可以在不同页面间保持状态同步。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/21864.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

jdk的组成和跨平台原理

为什么 1.笔试会用到 2. 方便理解程序的运行 java跨平台的原因: sun公司提供了各种平台可以使用的jvm,所以java将程序一次编译成字节码之后可以给各种平台运行。这也是java这么多年深受欢迎的原因

c++ lambda学习

一. 书写形式 [ capture clause ] (parameters) -> return-type { definition of method } [ 捕获 ] ( 参数列表 ) -> 返回类型 { 函数定义 } return-type返回值一般可以推导出来, 可以不用写, 所以可以简化为 [ capture clause ] (parameters) { definition of meth…

循环购模式!增加用户复购的不二之选!

大家好,我是吴军,来自一家专注于软件开发与商业模式设计的公司。我们主要业务是构建商城系统,并为各类企业提供全面的商业模式解决方案。目前,我们已经成功开发了超过200种独特的商业模式,帮助许多企业实现了他们的商业…

TCP协议的核心机制

TCP协议的核心机制 一:确认应答机制1.2:超时重传接收缓冲区 超时重传时间重置连接 一:确认应答机制 对于TCP协议来说,要解决的一个很重要的问题,就是可靠传输 可靠传输,不是指发送方能够100%的把数据发送给接收方,而是尽可能. 尤其是让发送方知道,接收方是否收到. 举个例子: …

MySQL 数据库实验

前言 数据库实验是抽的,所以仅供大家参考哦! 查看方式 上传到云盘里面了,大家可以下载( •̀ ω •́ )y https://www.alipan.com/s/WE9G9bGHdnV 提取码:7g6c

详解:重庆耶非凡的选品师项目有哪些优势?

在竞争激烈的电商市场中,重庆耶非凡科技有限公司凭借其独特的选品师项目,成功地在众多企业中脱颖而出。这一项目不仅体现了公司对市场趋势的敏锐洞察力,更彰显了其专业的选品能力和对消费者需求的深刻理解。 首先,耶非凡的选品师项…

大模型时代的具身智能系列专题(七)

北大王鹤团队 王鹤,北京大学前沿计算研究中心助理教授,本科毕业于清华大学,博士毕业于斯坦福大学,师从美国三院院士Leonidas. J Guibas教授。他创立并领导了具身感知与交互实验室(EPIC Lab),实验室立足三维视觉感知与…

MyBatis的各种查询功能

1、查询: 查询的标签select必须设置属性resultType或resultMap,用于设置实体类和数据库表的映射关系 resultType:自动映射,用于属性名和表中字段名一致的情况 resultMap:自定义映射,用于一对多或多对一或…

mysql 索引和null值的关系

在MySQL中,索引与NULL值的处理涉及一些特殊规则和注意事项,这些规则影响着索引的使用效率和查询优化器的决策: 索引中NULL值的包容性 允许NULL值:MySQL允许在唯一索引(Unique Index)和普通索引&#xff0…

Github生成SSH密钥,使用SSH进行连接

目录 一、生成新的SSH密钥 二、添加新的SSH密钥 三、测试SSH连接 四、SSH密钥密码 五、创建新仓库并推送到github 说明 使用 SSH URL 将 git clone、git fetch、git pull 或 git push 执行到远程存储库时, 须在计算机上生成 SSH 密钥对,并将公钥添加到…

1. 数据结构

文章目录 数据结构一、线性结构和非线性结构1. 线性结构2. 非线性结构 二、数组(Array)1. 定义2. 初始化数组 2. 链表(Linked List)3. 栈(Stack)4. 队列(Queue)5. 树(Tre…

(CVPRW,2024)可学习的提示:遥感领域小样本语义分割

文章目录 相关资料摘要引言方法训练基础类别新类别推理 相关资料 论文:Learnable Prompt for Few-Shot Semantic Segmentation in Remote Sensing Domain 代码:https://github.com/SteveImmanuel/OEM-Few-Shot-Learnable-Prompt 摘要 小样本分割是一项…

STP----生成树协议

目的:解决二层环路问题 跨层封装 广播风暴---广播帧在二层环路中形成逆时针和顺时针转动环路,并且无限循环,最终造成设备宕机,网络瘫痪。 MAC地址表的翻摆(漂移)---同一个数据帧,顺时针接收后记…

GUN compiler collection源代码编译过程

第一部分: 学习kernel需要了解编译的一些过程,为了详细理解GCC编译过程的原理,动手做了个sy,记录如下,有需要的童鞋可以参考。 1.环境:(均可,二次环境并非是WSL版本) r…

大模型应用框架-LangChain

LangChain的介绍和入门 💥 什么是LangChain LangChain由 Harrison Chase 创建于2022年10月,它是围绕LLMs(大语言模型)建立的一个框架,LLMs使用机器学习算法和海量数据来分析和理解自然语言,GPT3.5、GPT4是…

[LitCTF 2024 公开赛道] Crypto/PWN/Rev

DAS是打不动了,只能玩玩新生赛了。 Crypto small_e e3对密文直接开3次方 m [iroot(i,3)[0] for i in c_list] bytes(m) #bLitCTF{you_know_m_equ4l_cub3_root_0f_n}common_primes n1,n2有公共因子,用gcd求,再解RSA >>> p gc…

Android handler 一次通关

前言 Android 的 Handler 是一个用于管理线程间通信的工具,主要用于在不同的线程之间发送和处理消息。它是 Android 应用程序中处理异步任务的重要组成部分,尤其是在需要在后台线程进行操作,而操作结果需要在主线程(UI 线程)中更新界面时。 Handler 的基本概念 消息(Me…

一维时间序列信号的奇异小波时频分析方法(Python)

最初的时频分析技术就是短时窗傅里叶变换STFT,由于时窗变短,可供分析的信号量减少,采用经典的谱估算方法引起的误差所占比重会增加。且该短时窗一旦选定.则在整个变换过程中其时窗长度是固定的。变换后的时频分辨率也即固定&#…

第十五届蓝桥杯物联网试题(省赛)

这个省赛题不算难,中规中矩,记得看清A板B板,还有ADC的获取要配合定时器

视频修复工具助你完成高质量的视频作品!

在短视频发展兴起的时代,各种视频层出不穷的出现在了视野中,人们已经从追求数量转向追求质量。内容相同的视频,你视频画质好、质量高的更受大家欢迎,那么如何制作高质量、高清晰度的视频呢?与您分享三个视频修复工具。…