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

相关文章

使用animation.css库快速实现CSS3旋转动画效果

《使用animation.css库快速实现CSS3旋转动画效果》随着Web技术的不断发展,动画效果已经成为了网页设计中不可或缺的一部分,本文将深入探讨animation.css的工作原理,如何使用以及... 目录1. css3动画技术简介2. animation.css库介绍2.1 animation.cs

使用雪花算法产生id导致前端精度缺失问题解决方案

《使用雪花算法产生id导致前端精度缺失问题解决方案》雪花算法由Twitter提出,设计目的是生成唯一的、递增的ID,下面:本文主要介绍使用雪花算法产生id导致前端精度缺失问题的解决方案,文中通过代... 目录一、问题根源二、解决方案1. 全局配置Jackson序列化规则2. 实体类必须使用Long封装类3.

Python文件操作与IO流的使用方式

《Python文件操作与IO流的使用方式》:本文主要介绍Python文件操作与IO流的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、python文件操作基础1. 打开文件2. 关闭文件二、文件读写操作1.www.chinasem.cn 读取文件2. 写

PyQt6中QMainWindow组件的使用详解

《PyQt6中QMainWindow组件的使用详解》QMainWindow是PyQt6中用于构建桌面应用程序的基础组件,本文主要介绍了PyQt6中QMainWindow组件的使用,具有一定的参考价值,... 目录1. QMainWindow 组php件概述2. 使用 QMainWindow3. QMainW

使用Python自动化生成PPT并结合LLM生成内容的代码解析

《使用Python自动化生成PPT并结合LLM生成内容的代码解析》PowerPoint是常用的文档工具,但手动设计和排版耗时耗力,本文将展示如何通过Python自动化提取PPT样式并生成新PPT,同时... 目录核心代码解析1. 提取 PPT 样式到 jsON关键步骤:代码片段:2. 应用 JSON 样式到

java变量内存中存储的使用方式

《java变量内存中存储的使用方式》:本文主要介绍java变量内存中存储的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、介绍2、变量的定义3、 变量的类型4、 变量的作用域5、 内存中的存储方式总结1、介绍在 Java 中,变量是用于存储程序中数据

关于Mybatis和JDBC的使用及区别

《关于Mybatis和JDBC的使用及区别》:本文主要介绍关于Mybatis和JDBC的使用及区别,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、JDBC1.1、流程1.2、优缺点2、MyBATis2.1、执行流程2.2、使用2.3、实现方式1、XML配置文件

macOS Sequoia 15.5 发布: 改进邮件和屏幕使用时间功能

《macOSSequoia15.5发布:改进邮件和屏幕使用时间功能》经过常规Beta测试后,新的macOSSequoia15.5现已公开发布,但重要的新功能将被保留到WWDC和... MACOS Sequoia 15.5 正式发布!本次更新为 Mac 用户带来了一系列功能强化、错误修复和安全性提升,进一步增

Java资源管理和引用体系的使用详解

《Java资源管理和引用体系的使用详解》:本文主要介绍Java资源管理和引用体系的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、Java的引用体系1、强引用 (Strong Reference)2、软引用 (Soft Reference)3、弱引用 (W

ubuntu系统使用官方操作命令升级Dify指南

《ubuntu系统使用官方操作命令升级Dify指南》Dify支持自动化执行、日志记录和结果管理,适用于数据处理、模型训练和部署等场景,今天我们就来看看ubuntu系统中使用官方操作命令升级Dify的方... Dify 是一个基于 docker 的工作流管理工具,旨在简化机器学习和数据科学领域的多步骤工作流。