SwiftUI五视图动画和转场

代码下载

使用SwiftUI可以把视图状态的改变转成动画过程,SwiftUI会处理所有复杂的动画细节。在这篇中,会给跟踪用户徒步的图表视图添加动画,使用animation(_:)修改器给一个视图添加动画效果非常容易。

下载起步项目并跟着本篇教程一步步实践,或者查看本篇完成状态时的工程代码去学习,项目文件。

添加 Hiking 数据到应用程序

在添加动画之前,需要一些东西来做动画。在本节中,将导入和建模 Hiking 数据,然后添加一些预构建的视图,以便在图中静态地显示该数据。
请添加图片描述

1、从下载文件的“Resources”文件夹将 hikeData.json 文件拖动放入项目。在单击 Finish 之前,请确保选择 “Copy items if need”。

2、新建 Hike.swift 文件,与Landmark结构体一样,Hike结构体也遵守Codable协议,并且具有与相应数据文件中的键匹配的属性:

import Foundationstruct Hike: Codable, Hashable, Identifiable {var id: Intvar name: Stringvar distance: Doublevar difficulty: Intvar observations: [Observation]static var formatter = LengthFormatter()var distanceText: String {Hike.formatter.string(fromValue: distance, unit: .kilometer)}struct Observation: Codable, Hashable {var distanceFromStart: Doublevar elevation: Range<Double>var pace: Range<Double>var heartRate: Range<Double>}
}

3、新建 ModelData.swift 文件:

import Foundation@Observable
class ModelData {var hikes: [Hike] = load("hikeData.json")
}func load<T: Decodable>(_ filename: String) -> T {guard let path = Bundle.main.url(forResource: filename, withExtension: nil),let data = try? Data(contentsOf: path),let result = try? JSONDecoder().decode(T.self, from: data) else {fatalError("数据加载失败!")}return result
}

4、将已下载文件的Resources文件夹中的hike文件夹拖到项目中。在单击Finish之前,请确保选择“Copy items if need”和“Create groups”。熟悉这些新的视图,它们一起工作来显示加载到模型中的 hike 数据。

5、在HikeView.swift中,打开实时预览,体验一下图表的打开和隐藏,此时的状态改变时是没有添加动画效果的。在本篇的实践中,保持实时预览一直打开,每一步修改的效果就可以实时的看到。

给每个视图单独添加动画

在视图上使用animation(_:)修改器时,SwiftUI会在视图的任何可进行动画的属性发生改变时产生对应的动画效果。视图的颜色、不透明度、旋转角度、大小及一些其它属性都是可进行动画的。
请添加图片描述

1、在HikeView.swift中,给显示/隐藏切换的箭头按钮添加旋转动画,会发现现在按钮点击时的旋转有一个动画过渡的效果了。当视图从隐藏到展示时,让切换按钮变大1.5倍,把动画的类型从easeInOut改为spring()。SwiftUI包含一些预设或可自定义的动画类型,像弹簧(spring)动画和类型液体(fluid)动画类型。可以调整动画开始前的等待时长、动画的速度也可以指定让动画循环重复的进行:

struct HikeView: View {var hike: Hike@State private var showDetail = falsevar body: some View {VStack {HStack {HikeGraph(hike: hike, path: \.elevation).frame(width: 50, height: 30)VStack(alignment: .leading) {Text(hike.name).font(.headline)Text(hike.distanceText)}Spacer()Button {showDetail.toggle()} label: {Label("Graph", systemImage: "chevron.right.circle").labelStyle(.iconOnly).imageScale(.large).rotationEffect(.degrees(showDetail ? 90 : 0)).scaleEffect(showDetail ? 1.5 : 1).padding().animation(.spring())}}if showDetail {HikeDetail(hike: hike)}}}
}

2、如果只想让按钮具有缩放动画而不进行旋转动画,可以在scaleEffect前面添加animation(nil)来实现。可以在这里做一些实验,如果把其它的一些动画效果结合在一起,会怎么样:

struct HikeView: View {var hike: Hike@State private var showDetail = falsevar body: some View {VStack {HStack {HikeGraph(hike: hike, path: \.elevation).frame(width: 50, height: 30)VStack(alignment: .leading) {Text(hike.name).font(.headline)Text(hike.distanceText)}Spacer()Button {showDetail.toggle()} label: {Label("Graph", systemImage: "chevron.right.circle").labelStyle(.iconOnly).imageScale(.large).rotationEffect(.degrees(showDetail ? 90 : 0)).animation(nil).scaleEffect(showDetail ? 1.5 : 1).padding().animation(.spring())}}if showDetail {HikeDetail(hike: hike)}}}
}

3、进行下一节之前,把本节中添加的animation(_:)修改器都去掉。

把视图的状态改态转化成动画效果

已经学会了给单个视图添加动画的方法,现在可以学习怎么在视图的状态发生改变时添加动画效果。当用户点击按钮时会切换showDetail状态的值,在视图变化过程中添加动画效果。
请添加图片描述

1、把showDetail.toggle()包裹在withAnimation函数调用块中。showDetail的改变影响了视图HikeDetail和详情切换按钮,在显示/隐藏详情的过程中都有了过滤动画效果。

2、放慢动画速度,可以观察SwiftUI动画在被中断下是怎么运作的。给withAnimation传入一个时长4秒的基本动画参数.easeInOut(duration:4),可以指定动画过程时长,给withAnimation传入的动画参数与.animation(_:)修改器可用参数一致。

struct HikeView: View {var hike: Hike@State private var showDetail = falsevar body: some View {VStack {HStack {HikeGraph(hike: hike, path: \.elevation).frame(width: 50, height: 30)VStack(alignment: .leading) {Text(hike.name).font(.headline)Text(hike.distanceText)}Spacer()Button {withAnimation(.easeInOut(duration: 4)) {showDetail.toggle()}} label: {Label("Graph", systemImage: "chevron.right.circle").labelStyle(.iconOnly).imageScale(.large).rotationEffect(.degrees(showDetail ? 90 : 0)).scaleEffect(showDetail ? 1.5 : 1).padding()}}if showDetail {HikeDetail(hike: hike)}}}
}

3、在动画过程进行中点击按钮切换视图状态,查看对应的动画被中断时的效果。进行下一节之前,把动画时长参数(.easeInOut(duration: 4))去掉,让动画不再缓慢进行。

定制视图转场动画

默值情况下,视图离屏和入屏时的动画效果是渐隐/渐现, 这个默认的转场效果可以使用transition(_:)修改器进行定制。

1、给HikeView视图添加transition(_:)修改器,并定制转场参数为.slide,转场动画为滑入/滑出:

struct HikeView: View {var hike: Hike@State private var showDetail = falsevar body: some View {VStack {HStack {HikeGraph(hike: hike, path: \.elevation).frame(width: 50, height: 30)VStack(alignment: .leading) {Text(hike.name).font(.headline)Text(hike.distanceText)}Spacer()Button {withAnimation {showDetail.toggle()}} label: {Label("Graph", systemImage: "chevron.right.circle").labelStyle(.iconOnly).imageScale(.large).rotationEffect(.degrees(showDetail ? 90 : 0)).scaleEffect(showDetail ? 1.5 : 1).padding()}}if showDetail {HikeDetail(hike: hike).transition(.slide)}}}
}

2、可以把滑入/滑出这种转场动画封装起来,方便其它视图复用同样的转场效果:

extension AnyTransition {static var moveAndFade: AnyTransition {.slide}
}struct HikeView: View {var hike: Hike@State private var showDetail = falsevar body: some View {VStack {HStack {HikeGraph(hike: hike, path: \.elevation).frame(width: 50, height: 30)VStack(alignment: .leading) {Text(hike.name).font(.headline)Text(hike.distanceText)}Spacer()Button {withAnimation {showDetail.toggle()}} label: {Label("Graph", systemImage: "chevron.right.circle").labelStyle(.iconOnly).imageScale(.large).rotationEffect(.degrees(showDetail ? 90 : 0)).scaleEffect(showDetail ? 1.5 : 1).padding()}}if showDetail {HikeDetail(hike: hike).transition(AnyTransition.moveAndFade)}}}
}

3、在moveAndFade转场效果的定义中使用move(edge:),让滑入/滑出从屏幕的同一边进行:

extension AnyTransition {static var moveAndFade: AnyTransition {.move(edge: .trailing)}
}

4、使用asymmetric(insertion:removal:)修改器来定制视图显示/消失时的转场动画效果:

extension AnyTransition {static var moveAndFade: AnyTransition {let insertion = self.move(edge: .trailing).combined(with: .opacity)let removal = self.scale.combined(with: .opacity)return self.asymmetric(insertion: insertion, removal: removal)}
}

组合复杂的动画效果

点击图表下面的三个按钮,会在三个不同的数据集间进行切换并展示。本节中会使用组合动画,让图表在不同数据集间切换时的转换动画流畅自然。
请添加图片描述
1、把showDetail的默认值改为true,并把HikeView的预览模式视图固定在画布上。这样可以在编辑其它文件时,依然看到动画效果的变化。

2、在HikeGraph.swift中定义了一个新的波动动画,并把它与滑入/滑出动画一起应用到图表视图上:

extension Animation {static func ripple() -> Animation {Animation.default}
}struct HikeGraph: View {var hike: Hikevar path: KeyPath<Hike.Observation, Range<Double>>var color: Color {switch path {case \.elevation:return .graycase \.heartRate:return Color(hue: 0, saturation: 0.5, brightness: 0.7)case \.pace:return Color(hue: 0.7, saturation: 0.4, brightness: 0.7)default:return .black}}var body: some View {let data = hike.observationslet overallRange = rangeOfRanges(data.lazy.map { $0[keyPath: path] })let maxMagnitude = data.map { magnitude(of: $0[keyPath: path]) }.max()!let heightRatio = 1 - CGFloat(maxMagnitude / magnitude(of: overallRange))return GeometryReader { proxy inHStack(alignment: .bottom, spacing: proxy.size.width / 120) {ForEach(Array(data.enumerated()), id: \.offset) { index, observation inGraphCapsule(index: index,color: color,height: proxy.size.height,range: observation[keyPath: path],overallRange: overallRange).animation(.ripple())}.offset(x: 0, y: proxy.size.height * heightRatio)}}}
}

3、把动画切换为弹簧动画(spring),并设置弹簧阻尼系数为0.5,动画过程中产生了逐渐回弹效果:

extension Animation {static func ripple() -> Animation {Animation.spring(dampingFraction: 0.5)}
}

4、加速弹簧动画的执行速度,缩短切换图表的时间:

extension Animation {static func ripple() -> Animation {Animation.spring(dampingFraction: 0.5).speed(2)}
}

5、以当条形在图表中的位置为参数,添加延迟效果,图表中的每个条形会顺序动起来:

extension Animation {static func ripple(index: Int) -> Animation {Animation.spring(dampingFraction: 0.5).speed(2).delay(Double(index)*0.03)}
}struct HikeGraph: View {var hike: Hikevar path: KeyPath<Hike.Observation, Range<Double>>var color: Color {switch path {case \.elevation:return .graycase \.heartRate:return Color(hue: 0, saturation: 0.5, brightness: 0.7)case \.pace:return Color(hue: 0.7, saturation: 0.4, brightness: 0.7)default:return .black}}var body: some View {let data = hike.observationslet overallRange = rangeOfRanges(data.lazy.map { $0[keyPath: path] })let maxMagnitude = data.map { magnitude(of: $0[keyPath: path]) }.max()!let heightRatio = 1 - CGFloat(maxMagnitude / magnitude(of: overallRange))return GeometryReader { proxy inHStack(alignment: .bottom, spacing: proxy.size.width / 120) {ForEach(Array(data.enumerated()), id: \.offset) { index, observation inGraphCapsule(index: index,color: color,height: proxy.size.height,range: observation[keyPath: path],overallRange: overallRange).animation(.ripple(index: index))}.offset(x: 0, y: proxy.size.height * heightRatio)}}}
}

观察一下自定义波动(rippling)效果是怎么作用在视图转场中的。

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

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

相关文章

单元测试覆盖率

什么是单元测试覆盖率 关于其定义&#xff0c;先来看一下维基百科上的一段描述&#xff1a; 代码覆盖&#xff08;Code coverage&#xff09;是软件测试中的一种度量&#xff0c;描述程序中源代码被测试的比例和程度&#xff0c;所得比例称为代码覆盖率。 简单来理解&#xff…

【Redis学习笔记05】Jedis客户端(中)

Jedis客户端 1. 命令 1.1 String类型 1.1.1 常见命令 SET命令 语法&#xff1a;SET key value [EX seconds | PX milliseconds] [NX|XX] 说明&#xff1a;将string类型的value值设置到指定key中&#xff0c;如果之前该key存在&#xff0c;则会覆盖原先的值&#xff0c;原先…

短剧看剧系统投流版系统搭建,前端uni-app

目录 前言&#xff1a; 一、短剧看剧系统常规款短剧系统和投流版的区别&#xff1f; 二、后端体系 1.管理端&#xff1a; 2.代理投流端 三、功能区别 总结&#xff1a; 前言&#xff1a; 23年上半年共上新微短剧481部&#xff0c;相较于2022年全年上新的454部&#xff0…

C语言王国——数据的内存管理

目录 一、引言 二、整形在内存中的存储 2.1 进制之间的转换 2.1.1 整形的二进制 2.1.2 十进制和二进制 2.1.3 十进制和八进制的转换 2.1.4 十六进制和十进制的转换 2.2 原码&#xff0c;反码&#xff0c;和补码 三、大、小端字节序 3.1 大小端的定义 3.2 为什么会有大…

高考后志愿填报信息采集系统制作指南

在高考的硝烟散去之后&#xff0c;每位学生都面临着一个重要的任务——志愿填报。老师们如何高效、准确地收集和整理这些信息&#xff0c;成为了一个棘手的问题。难道我们只能依赖传统的手工登记方式&#xff0c;忍受其繁琐和易错吗&#xff1f; 易查分是一个简单易用的在线工具…

容器中运行ping提示bash: ping: command not found【笔记】

容器中运行ping提示bash: ping: command not found 原因是容器中没有安装ping命令 在容器中安装ping命令&#xff0c;可以使用以下命令&#xff1a; 对于基于Debian/Ubuntu的容器&#xff0c;使用以下命令&#xff1a; apt-get update apt-get install -y iputils-ping对于基…

上位机图像处理和嵌入式模块部署(f407 mcu和其他mcu品类的选择)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 很多朋友读书的时候学的是stm32&#xff0c;工作中用的也是stm32。这本来问题不大&#xff0c;但是过去两三年的经历告诉我们&#xff0c;mcu的使用…

音频数据上的会话情感分析

情感分析&#xff0c;也被称为观点挖掘&#xff0c;是自然语言处理(NLP)中一个流行的任务,因为它有着广泛的工业应用。在专门将自然语言处理技术应用于文本数据的背景下,主要目标是训练出一个能够将给定文本分类到不同情感类别的模型。下图给出了情感分类器的高级概述。 例如,三…

牛客热题:最长公共子序列Ⅱ

&#x1f4df;作者主页&#xff1a;慢热的陕西人 &#x1f334;专栏链接&#xff1a;力扣刷题日记 &#x1f4e3;欢迎各位大佬&#x1f44d;点赞&#x1f525;关注&#x1f693;收藏&#xff0c;&#x1f349;留言 文章目录 牛客热题&#xff1a;最长公共子序列Ⅱ题目链接方法一…

网络安全形势与WAF技术分享

我一个朋友的网站&#xff0c;5月份时候被攻击了&#xff0c;然后他找我帮忙看看&#xff0c;我看他的网站、网上查资料&#xff0c;不看不知道&#xff0c;一看吓一跳&#xff0c;最近几年这网络安全形势真是不容乐观&#xff0c;在网上查了一下资料&#xff0c;1、中国信息通…

vue2的form利用插槽修改错误提示UI

1. 需求 很多时候我们使用el-form想修改下错误提示的UI&#xff0c;比如table中使用form校验这类场景下错误提示的UI调整就非常重要。 2. 了解文档 Form-Item Scoped Slot name说明error自定义表单校验信息的显示方式&#xff0c;参数为 { error } 3.实际使用 html里使用…

Python Flask实现蓝图Blueprint配置和模块渲染

Python基础学习&#xff1a; Pyhton 语法基础Python 变量Python控制流Python 函数与类Python Exception处理Python 文件操作Python 日期与时间Python Socket的使用Python 模块Python 魔法方法与属性 Flask基础学习&#xff1a; Python中如何选择Web开发框架&#xff1f;Pyth…

数据结构笔记 4 树和二叉树

二叉树和完全二叉树的区别&#xff1f; 二叉树和完全二叉树的主要区别在于它们的结构特性和节点排列方式&#xff1a; 1. **二叉树**&#xff1a; - 是一种数据结构&#xff0c;其中每个节点最多有两个子节点&#xff0c;通常称为左子节点和右子节点。 - 节点的子节点数量…

git凭证

默认是manager # 将凭证缓存到内存中&#xff0c;默认缓存15分钟 git config --global credential.helper cache# 将凭证存储到磁盘上的纯文本文件中 git config --global credential.helper store# 使用 Git 凭证管理器 git config --global credential.helper manager-core查…

图文详解Windows系统下搭建mysql开发环境——mysql Community 8 和 navicat Premium 17 的安装和使用

在正式开始学习使用MySQL之前&#xff0c;我们有必要先搭建一个良好的开发环境&#xff0c;让我们的学习和工作效率事半功倍。 本文涉及到的软件百度云盘&#xff1a;链接&#xff1a;https://pan.baidu.com/s/1jj_YajEv8adeEjMrXLhOTQ?pwd1023 提取码&#xff1a;1023 目录 …

元宇宙数字藏品交易所,未来发展的大趋势

随着科技的飞速进步&#xff0c;元宇宙以其独特的魅力为数字世界绘制了一幅前所未有的宏伟蓝图。在这一宏大的背景下&#xff0c;数字藏品交易所作为连接虚拟与现实的桥梁&#xff0c;正以其卓越的优势&#xff0c;引领着数字藏品市场迈向新的高度。 首先&#xff0c;元宇宙为…

三十六篇:未来架构师之道:掌握现代信息系统典型架构

未来架构师之道&#xff1a;掌握现代信息系统典型架构 1. 引言 在企业的数字化转型浪潮中&#xff0c;信息系统架构的角色变得日益重要。它不仅承载了企业的IT战略&#xff0c;更是确保企业在复杂、动态的市场环境中稳定运行的关键。作为信息系统的骨架&#xff0c;一个精心设…

HTML5+CSS3+JS小实例:网格图库

实例:网格图库 技术栈:HTML+CSS+JS 效果: 源码: 【HTML】 <!DOCTYPE html> <html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0&…

Python中猴子补丁是什么,如何使用

1、猴子补丁奇遇记 &#x1f412; 在Python的世界深处&#xff0c;隐藏着一种神秘而又强大的技巧——猴子补丁&#xff08;Monkey Patching&#xff09;。这是一项允许你在程序运行时动态修改对象&#xff08;如模块、类或函数&#xff09;的行为的技术。它得名于其“快速修补…

算法导论实战(六)(算法导论习题三十四、三十五章)

&#x1f308; 个人主页&#xff1a;十二月的猫-CSDN博客 &#x1f525; 系列专栏&#xff1a; &#x1f3c0;算法启示录 &#x1f4aa;&#x1f3fb; 十二月的寒冬阻挡不了春天的脚步&#xff0c;十二点的黑夜遮蔽不住黎明的曙光 前言 算法导论的知识点学习将持续性更新在算…