SwiftUI七使用UI控件

2024-06-12 07:04
文章标签 使用 ui 控件 swiftui

本文主要是介绍SwiftUI七使用UI控件,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

代码下载

在应用中,用户可以创建一个简介来描述他们自已的个人情况。为了让用户可以编辑自己的简介,需要添加一个编辑模式并设计一个偏好设置界面。这里使用多种通用控件来展示用户的各种数据,并在用户保存他们所做的数据修改时更新地标数据模型。

按照步骤在下面的项目工程中一步步进行实践,项目文件。

展示用户简介

应用在本地存储了一些配置和用户偏好设置。在用户编辑这些数据前,会被展示在一个没有编辑按钮的概要视图上。
请添加图片描述

1、在工程的 Model 组中创建一个名为Profile.swift的文件,并在这个新文件中定义一个用户配置:

struct Profile {var username: Stringvar prefersNotifications = truevar seasonalPhoto = Season.wintervar goalDate = Date()static let `default` = Profile(username: "g_kumar")enum Season: String, CaseIterable, Identifiable {case spring = "🌷"case summer = "🌞"case autumn = "🍂"case winter = "☃️"var id: String { rawValue }}
}

2、接下来,在Views组下创建一个名为Profiles的新组,然后向该组添加一个名为ProfileHost的视图,该视图带有显示存储的概要文件的用户名的文本视图。ProfileHost视图将承载概要信息的静态摘要视图和编辑模式。在稍后引入模型数据 Profile 之前,可以将这里的 draftProfile 设置为默认的 Profile 作为占位符:

struct ProfileHost: View {@State var draftProfile = Profile.defaultvar body: some View {Text("Profile for: \(draftProfile.username)")}
}

3、在Profiles组中创建另一个名为 ProfileSummary 的视图,该视图会持有一个Profile实例,并显示用户的基本信息。Profile概要视图持有一个Profile对像的原因是,因为它的父视图ProfileHost管理着视图的状态,它不能与Profile进行绑定:

struct ProfileSummary: View {var profile: Profilevar body: some View {ScrollView {VStack(alignment: .leading, spacing: 10) {Text(profile.username).bold().font(.title)Text("Notifications: \(profile.prefersNotifications ? "On": "Off" )")Text("Seasonal Photos: \(profile.seasonalPhoto.rawValue)")Text("Goal Date: ") + Text(profile.goalDate, style: .date)}}}
}

4、更新ProfileHost文件,显示新的概要视图:

struct ProfileHost: View {@State var draftProfile = Profile.defaultvar body: some View {VStack(alignment: .leading, spacing: 20) {ProfileSummary(profile: draftProfile)}.padding()}
}

5、在 Hikes 文件夹中创建一个名为HikeBadge的新视图,这个新视图由Badge视图和一些描述性文字构成。Badge仅仅是一个图形,在HikeBadge视图中的文本与accessibility(label:)属性修改器一起,可以让这个徽章对用户更加清晰。注意frame(width:height:)的两种不同的用法用来配置徽章以不同的缩放尺寸显示,徽章的绘制逻辑产生的结果取决于它所呈现的frame的大小。为了确保理想的外观,在300 x 300点的frame中渲染。要获得最终图形所需的尺寸,然后缩放渲染结果并将其放置在相对较小的frame中:

struct HikeBadge: View {var name: Stringvar body: some View {VStack {Badge().frame(width: 300, height: 300).scaleEffect(1.0/3.0).frame(width: 100, height: 100)Text(name).font(.caption).accessibilityLabel("Badge for \(name).")}}
}

6、更新ProfileSummary文件,添加几个不同的徽章代表用户得到的不同徽章。通过包含HikeView来完成ProfileSummary。为了使用 hike 数据,还需要添加一个ModelData环境属性:

struct ProfileSummary: View {@Environment(ModelData.self) var modelDatavar profile: Profilevar body: some View {ScrollView {VStack(alignment: .leading, spacing: 10) {Text(profile.username).bold().font(.title)Text("Notifications: \(profile.prefersNotifications ? "On": "Off" )")Text("Seasonal Photos: \(profile.seasonalPhoto.rawValue)")Text("Goal Date: ") + Text(profile.goalDate, style: .date)Divider()VStack(alignment: .leading) {Text("Completed Badges").font(.headline)ScrollView(.horizontal) {HStack {HikeBadge(name: "First Hike")HikeBadge(name: "Earth Day").hueRotation(Angle(degrees: 90))HikeBadge(name: "Tenth Hike").grayscale(0.5).hueRotation(Angle(degrees: 45))}.padding(.bottom)}}Divider()VStack {Text("")HikeView(hike: ModelData().hikes[0])}}}}
}#Preview {ProfileSummary(profile: Profile.default).environment(ModelData())
}

7、在CategoryHome中,使用toolbar修改器向导航栏添加一个用户配置文件按钮,并在用户点击它时显示ProfileHost视图,添加listStyle修改器以选择更适合内容的列表样式:

struct CategoryHome: View {@Environment(ModelData.self) var modelData: ModelData@State private var showingProfile = falsevar body: some View {NavigationSplitView {List {modelData.features[0].image.resizable().scaledToFill().frame(height: 200).clipped().listRowInsets(EdgeInsets())ForEach(modelData.categories.keys.sorted(), id: \.self) { key inCategoryRow(categoryName: key, items: modelData.categories[key] ?? [])}.listRowInsets(EdgeInsets())}.listStyle(.inset).navigationTitle("Featured").toolbar(content: {Button {showingProfile.toggle()} label: {Label("User Profile", systemImage: "person.crop.circle")}}).sheet(isPresented: $showingProfile, content: {ProfileHost().environment(modelData)})} detail: {Text("Select a Landmark")}}
}

添加编辑模式

用户需要能够在浏览模式和编辑模式之间进行切换来查看或者修改用户简介的信息。通过在ProfileHost上添加一个Edit Button,然后创建一个用来编辑简介信息的页面。
请添加图片描述

1、选择ProfileHost并将模型数据作为环境属性添加到预览中。尽管这个视图没有使用带有@Environment属性包装器的属性,但是这个视图的子视图ProfileSummary使用了。因此,如果没有这个修改器,预览将失败。

2、添加一个环境视图属性,该属性与环境的.editmode无关。SwiftUI在环境中可以使用@Environment属性包装器对访问的值提供存储。前面使用@Environment来检索存储在环境中的类。在这里,使用它来访问内置于环境中的editMode值,以读取或写入编辑作用域。

3、创建一个Edit按钮,用于打开和关闭环境的editMode值。EditButton控制在上一步中访问的editMode环境值。

struct ProfileHost: View {@Environment(\.editMode) var editMode@State var draftProfile = Profile.defaultvar body: some View {VStack(alignment: .leading, spacing: 20) {ProfileSummary(profile: draftProfile)}.padding()}
}#Preview {ProfileHost().environment(ModelData())
}

4、更新UserData类,包含一个Profile实例,即使用户简介页面消失后也可以存储编辑后的信息:

class ModelData {var landmarks: [Landmark] = load("landmarkData.json")var hikes: [Hike] = load("hikeData.json")var profile = Profile.defaultvar categories: [ String: [Landmark] ] {Dictionary(grouping: landmarks) { $0.category.rawValue }}var features: [Landmark] {landmarks.filter { $0.isFeatured }}
}

5、从环境变量中读取用户简介信息,并把数据传递给ProfileHost视图的控件上进行展示。为了在编辑状态下修改简介信息后确认修改前避免更新全局状态(例如在编辑用户名的过程中),编辑视图在一个备份属性中进行相应的修改操作,确认修改后,才把备份属性同步到全局应用状态中。

6、添加一个条件视图,可以用来显示静态用户简介视图或者是编辑模式的用户简介视图。当前的编辑模式只支持静态文本框的编辑。

struct ProfileHost: View {@Environment(\.editMode) var editMode@Environment(ModelData.self) var modelData@State var draftProfile = Profile.defaultvar body: some View {VStack {HStack {Spacer()EditButton()}if editMode?.wrappedValue == .inactive {ProfileSummary(profile: modelData.profile)} else {Text("Profile Editor")}}.padding()}
}

定义简介编辑器

用户简介编辑器包含几个单独的控件用来修改对应简介信息。在简介中,一些项例如徽章是不可以编辑修改的,所以它们不会出现在简介编辑器中。为了保持简介在编辑模式和浏览模式的一致性,需要按照简介页面各项相同的顺序进行添加。
请添加图片描述

1、创建一个名为ProfileEditor的新视图,并绑定用户简介中的草稿。视图中的第一个控件是TextField,用来更新用户名字段值。创建TextField时要提供一个标签和一个绑定字符串。

2、添加一个切换开关,用来设置用户是否接收相关地标事件的推送通知。这个Toggle控件打开和关闭正好对应着布尔值的true或false。

3、把一个Picker和一个Text放在VStack结构里,让这个地标可以选择不同季节。

4、最后,在季节图片选择器下方添加一个DatePicker,用来修改地标的目标浏览日期

struct ProfileEditor: View {@Binding var profile: Profilevar dateRange: ClosedRange<Date> {let min = Calendar.current.date(byAdding: .year, value: -1, to: profile.goalDate)!let max = Calendar.current.date(byAdding: .year, value: 1, to: profile.goalDate)!return min...max}var body: some View {List {HStack {Text("Username")Spacer()TextField("Username", text: $profile.username).foregroundStyle(.secondary).multilineTextAlignment(.trailing)}Toggle(isOn: $profile.prefersNotifications, label: {Text("Enable Notifications")})Picker("Seasonal Photo", selection: $profile.seasonalPhoto) {ForEach(Profile.Season.allCases) { season inText(season.rawValue).tag(season)}}DatePicker(selection: $profile.goalDate, in: dateRange, displayedComponents: .date) {Text("Goal Date")}}}
}#Preview {ProfileEditor(profile: .constant(.default))
}

更新ProfileHost中的条件内容,让它包含条件编辑器并把简单的绑定关系传递给简介编辑器。现在当你点击Edit按钮,简介视图就会变成编辑模式了。:

struct ProfileHost: View {@Environment(\.editMode) var editMode@Environment(ModelData.self) var modelData@State var draftProfile = Profile.defaultvar body: some View {VStack {HStack {Spacer()EditButton()}if editMode?.wrappedValue == .inactive {ProfileSummary(profile: modelData.profile)} else {ProfileEditor(profile: $draftProfile)}}.padding()}
}

延迟编辑传播

在编辑模式时,使用用户简介信息的备份进行修改,当用户确认进行修改后,再用修改的备份信息覆盖真正的用户信息。直到用户退出编辑模式前都不让编辑的备份生效。
请添加图片描述

1、在ProfileHost视图上添加一个取消按钮。不像编辑模式按钮提供的完成按钮,取消按钮不会应用修改后的简介备份信息到实际的简介数据上。

2、将正确的简介数据使用onAppear(perform:)和onDisappear(perform:)填充到编辑器,当用户点击完成按钮后持久更新用户简介数据。否则下一次进入编辑模式时,将使用上一次的用户简介数据来展示。

struct ProfileHost: View {@Environment(\.editMode) var editMode@Environment(ModelData.self) var modelData@State var draftProfile = Profile.defaultvar body: some View {VStack(alignment: .leading, spacing: 20) {HStack {if editMode?.wrappedValue == .active {Button("Cancel", role: .cancel) {draftProfile = modelData.profileeditMode?.animation().wrappedValue = .inactive}}Spacer()EditButton()}if editMode?.wrappedValue == .inactive {ProfileSummary(profile: modelData.profile)} else {ProfileEditor(profile: $draftProfile).onAppear(perform: {draftProfile = modelData.profile}).onDisappear(perform: {modelData.profile = draftProfile})}}.padding()}
}

这篇关于SwiftUI七使用UI控件的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1053510

相关文章

Java使用Mail构建邮件功能的完整指南

《Java使用Mail构建邮件功能的完整指南》JavaMailAPI是一个功能强大的工具,它可以帮助开发者轻松实现邮件的发送与接收功能,本文将介绍如何使用JavaMail发送和接收邮件,希望对大家有所... 目录1、简述2、主要特点3、发送样例3.1 发送纯文本邮件3.2 发送 html 邮件3.3 发送带

使用DeepSeek搭建个人知识库(在笔记本电脑上)

《使用DeepSeek搭建个人知识库(在笔记本电脑上)》本文介绍了如何在笔记本电脑上使用DeepSeek和开源工具搭建个人知识库,通过安装DeepSeek和RAGFlow,并使用CherryStudi... 目录部署环境软件清单安装DeepSeek安装Cherry Studio安装RAGFlow设置知识库总

Python FastAPI入门安装使用

《PythonFastAPI入门安装使用》FastAPI是一个现代、快速的PythonWeb框架,用于构建API,它基于Python3.6+的类型提示特性,使得代码更加简洁且易于绶护,这篇文章主要介... 目录第一节:FastAPI入门一、FastAPI框架介绍什么是ASGI服务(WSGI)二、FastAP

Spring-AOP-ProceedingJoinPoint的使用详解

《Spring-AOP-ProceedingJoinPoint的使用详解》:本文主要介绍Spring-AOP-ProceedingJoinPoint的使用方式,具有很好的参考价值,希望对大家有所帮... 目录ProceedingJoinPoijsnt简介获取环绕通知方法的相关信息1.proceed()2.g

Maven pom.xml文件中build,plugin标签的使用小结

《Mavenpom.xml文件中build,plugin标签的使用小结》本文主要介绍了Mavenpom.xml文件中build,plugin标签的使用小结,文中通过示例代码介绍的非常详细,对大家的学... 目录<build> 标签Plugins插件<build> 标签<build> 标签是 pom.XML

JAVA虚拟机中 -D, -X, -XX ,-server参数使用

《JAVA虚拟机中-D,-X,-XX,-server参数使用》本文主要介绍了JAVA虚拟机中-D,-X,-XX,-server参数使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有... 目录一、-D参数二、-X参数三、-XX参数总结:在Java开发过程中,对Java虚拟机(JVM)的启动参数进

Java中使用注解校验手机号格式的详细指南

《Java中使用注解校验手机号格式的详细指南》在现代的Web应用开发中,数据校验是一个非常重要的环节,本文将详细介绍如何在Java中使用注解对手机号格式进行校验,感兴趣的小伙伴可以了解下... 目录1. 引言2. 数据校验的重要性3. Java中的数据校验框架4. 使用注解校验手机号格式4.1 @NotBl

Python使用DeepSeek进行联网搜索功能详解

《Python使用DeepSeek进行联网搜索功能详解》Python作为一种非常流行的编程语言,结合DeepSeek这一高性能的深度学习工具包,可以方便地处理各种深度学习任务,本文将介绍一下如何使用P... 目录一、环境准备与依赖安装二、DeepSeek简介三、联网搜索与数据集准备四、实践示例:图像分类1.

Linux系统之authconfig命令的使用解读

《Linux系统之authconfig命令的使用解读》authconfig是一个用于配置Linux系统身份验证和账户管理设置的命令行工具,主要用于RedHat系列的Linux发行版,它提供了一系列选项... 目录linux authconfig命令的使用基本语法常用选项示例总结Linux authconfi

Windows server服务器使用blat命令行发送邮件

《Windowsserver服务器使用blat命令行发送邮件》在linux平台的命令行下可以使用mail命令来发送邮件,windows平台没有内置的命令,但可以使用开源的blat,其官方主页为ht... 目录下载blatBAT命令行示例备注总结在linux平台的命令行下可以使用mail命令来发送邮件,Win