SwiftUI中的组合动画(Simultaneous, Sequenced, Exclusive)

了解了常见的几种手势后,接下来我们了解一下组合手势的操作,当一个视图存在多个手势的时候,为了避免手势冲突,SwiftUI提供了自定义手势的方法,比如同时进行,顺序进行等等。

以下是一些常见的多种手势组合使用方式:

  • simultaneously(with:):同时使用多个手势,使它们可以同时响应用户的操作。例如,同时使用MagnificationGestureRotationGesture来实现不同的交互效果。
  • sequenced(before:):按顺序使用多个手势,确保它们按照特定的顺序依次执行。例如,先执行LongPressGesture,然后再执行DragGesture
  • exclusively(before:):在特定条件下使用不同的手势。例如,根据某个条件选择性地使用DragGestureTapGesture

simultaneously(with:) 同时进行

下面对一个Image同时添加旋转和放缩的手势,并且在操作的时候两个动画同时进行。
在这里插入图片描述
在上面代码中,我们将之前放缩和旋转动画的手势单独提了出来,定义成两个手势属性,这样更便于管理,也会更好阅读代码。关于放缩和旋转代码部分,在之前的文章已经做了详细的说明,这里就不再赘述了。

定义好两个手势属性后,我们给Image添加.gesture修饰符,并传入下面的组合手势:

magnificationGesture.simultaneously(with: rotationGesture)

或者:

rotationGesture.simultaneously(with: magnificationGesture)

因为是同时进行的两个手势,所以说谁组合谁都一样。

完整代码如下:

struct SimultaneousDemo: View {@GestureState private var scalingRatio: CGFloat = 1.0@State private var lastRatio: CGFloat = 1.0@GestureState private var rotateAngle: Angle = Angle(degrees: 0.0)@State private var lastAngle: Angle = Angle(degrees: 0.0)var magnificationGesture: some Gesture {MagnificationGesture().updating($scalingRatio, body: { value, state, _ instate = value}).onEnded({ value inlastRatio *= value})}var rotationGesture: some Gesture {RotationGesture().updating($rotateAngle, body: { value, state, _ instate = value}).onEnded({ value inlet newDegress = lastAngle.degrees + value.degreeslastAngle = Angle(degrees: newDegress)})}var body: some View {Image("liuyifei").resizable().frame(width: 200, height: 260).scaleEffect(scalingRatio).scaleEffect(lastRatio).rotationEffect(rotateAngle).rotationEffect(lastAngle).gesture(rotationGesture.simultaneously(with: magnificationGesture))}
}

sequenced(before:) 顺序执行

按顺序使用多个手势,确保它们按照特定的顺序依次执行。
例如下面这个示例,先执行LongPressGesture,然后再执行DragGesture
在这里插入图片描述
上面代码中同样是将两个手势单独提出来当作属性处理,另外为了测试效果,设置了最短的长按时间,按住时图片放大,1秒后图片变回原形,长按手势结束,此时拖动手势才生效。

再给Image添加的.gesture修饰符中传入下面的组合手势,这个可是分顺序的。

longPressGesture.sequenced(before: dragGesture)

完整代码如下:

struct SequencedDemo: View {@GestureState private var isLongPressing = false@State private var dragOffset: CGSize  = .zero@State private var position: CGSize = .zerovar longPressGesture: some Gesture {LongPressGesture(minimumDuration: 1).updating($isLongPressing, body: { currentState, state, _ instate = currentState})}var dragGesture: some Gesture {DragGesture().onChanged({ value indragOffset.width = position.width + value.translation.widthdragOffset.height = position.height + value.translation.height}).onEnded({ _ inposition = dragOffset})}var body: some View {Image("liuyifei").resizable().frame(width: 200, height: 260).scaleEffect(isLongPressing ? 1.5 : 1.0).offset(dragOffset).gesture(longPressGesture.sequenced(before: dragGesture))}
}

exclusively(before:)

这种手势组合方式可以根据条件来决定哪个手势应该被触发,从而实现更灵活的交互效果。

下面我演示如何在一个视图上根据条件选择性地使用DragGestureLongPressGesture手势。
为了证明长按是否生效了,这里加了一个弹框,长按生效后弹框。

在这里插入图片描述
上面的示例中,在.gesture修饰符中添加了:

longPressGesture.exclusively(before: dragGesture)

意思就是优先判断LongPressGesture是否满足,当用户长按视图时,达到长按手势的最短时间后,长按手势生效,此时DragGesture无效;如果未到长按手势的最短时间就拖拽,那么LongPressGesture失效。

之前的文章说过LongPressGesture执行时如果手指移动超过一定距离,那么LongPressGesture就不满足触发条件了,那么就失效了。

完整代码如下:

struct ExclusivelyDemo: View {@State private var dragOffset: CGSize  = .zero@State private var position: CGSize = .zero@State private var isLongPress: Bool = falsevar longPressGesture: some Gesture {LongPressGesture(minimumDuration: 1).onEnded { _ inisLongPress.toggle()}}var dragGesture: some Gesture {DragGesture().onChanged({ value indragOffset.width = position.width + value.translation.widthdragOffset.height = position.height + value.translation.height}).onEnded({ _ inposition = dragOffset})}var body: some View {Image("liuyifei").resizable().frame(width: 200, height: 260).offset(dragOffset).gesture(longPressGesture.exclusively(before: dragGesture)).alert(isPresented: $isLongPress, content: {Alert(title: Text("LongPress手势响应了"))})}
}

写在最后

本篇文章主要介绍了三种手势组合方式,并做了举例,组合方式也比较简单,仅供大家参考。

最后,希望能够帮助到有需要的朋友,如果觉得有帮助,还望点个赞,添加个关注,笔者也会不断地努力,写出更多更好用的文章。

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

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

相关文章

关于AI绘画的模型、开源项目、工具、技巧的学习

目录 一、AI绘画的大模型有哪些? 二、Stable Diffusion是一个流行的AI绘画开源项目。 三、AI绘画的开源工具有哪些? 四、AI绘画的技巧 五、最简单的实践 一、AI绘画的大模型有哪些? AI绘画领域中存在多种大模型,每种模型都有…

渗透测试 一个很奇怪的支付漏洞

新手实战刷课网站、好玩又有趣! 第一步 打开网站、任意账户名密码登陆发现验证码可重复利用 这时候我们可以试试admin账号、发现如果账号正确会提示账户已存在、反之回显账户密码错误 第二步 既然验证码可以重复利用;而且账号名有回显 这时候我们试…

学习使用博客记录生活

学习使用博客记录生活 新的改变 今天新的开始,让我用图片开始记录吧 看这个背景图片怎么样

人生苦短,我学python之数据类型(上)

个人主页:星纭-CSDN博客 系列文章专栏:Python 踏上取经路,比抵达灵山更重要!一起努力一起进步! 目录 一.元组 (tuple) 二.集合(set) 三.字典(dict) 一.元组 &#…

docker 清空所有镜像日志

Docker清空所有镜像日志流程 1. 查看当前运行的容器 首先,我们需要查看当前正在运行的容器,以确定需要清空日志的容器。 可以使用以下命令查看当前正在运行的容器: docker ps 1. 2. 停止所有运行中的容器 在清空镜像日志之前,我…

MySQL存储过程for循环处理查询结果

在MySQL数据库中,存储过程是一种预编译的SQL语句集,可以被多次调用。在MySQL中使用存储过程查询到结果后,有时候需要对这些结果进行循环处理。 1. 创建表 CREATE TABLE t_job (job_id int(11) unsigned NOT NULL AUTO_INCREMENT,job_name v…

深入了解银行信用卡催收系统

银行信用卡催收系统是一个专门用于管理和执行信用卡逾期账款催收工作的系统。该系统通常具备以下关键功能和特点: 智能呼叫系统:具备自动拨号功能,可以批量拨打逾期客户的电话,播放定制的催收录音信息或直接连接到人工坐席。此外…

崆峒酥饼:端午佳节的美味之选

崆峒酥饼:端午佳节的美味之选 在端午佳节来临之际,崆峒酥饼成为了备受瞩目的佳节之选。崆峒酥饼以其独特的制作工艺和口感,为这个传统节日增添了一份美味与温馨。 崆峒酥饼源自甘肃平凉,是当地的传统名点。它选用优质的面粉、油脂…

Linux——进程与线程

进程与线程 前言一、Linux线程概念线程的优点线程的缺点线程异常线程用途 二、Linux进程VS线程进程和线程 三、Linux线程控制创建线程线程ID及进程地址空间布局线程终止线程等待分离线程 四、习题巩固请简述什么是LWP请简述LWP与pthread_create创建的线程之间的关系简述轻量级进…

Java怎样动态给对象添加属性并赋值【代码实现】

本篇文章主要介绍Java如何给已有实体类动态的添加字段并返回新的实体对象且不影响原来的实体对象结构。 参考代码如下&#xff1a; 引入依赖包 <dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>2.2.2</…

云端升级,智能适配——LDR6282,USB-C接口显示器的最佳选择

华为MateView USB-C接口显示器技术深度解析与科普 随着科技的飞速发展&#xff0c;终端显示产品也迎来了全新的变革。在众多更新迭代中&#xff0c;华为MateView显示器凭借其独特的USB-C接口设计&#xff0c;为用户带来了前所未有的便捷体验。本文将带您深入探索这款显示器的技…

智能界面设计:数字孪生与大数据结合的美学典范

智能界面设计&#xff1a;数字孪生与大数据结合的美学典范 引言 在数字化浪潮的推动下&#xff0c;智能界面设计成为了连接用户与技术的重要桥梁。数字孪生技术与大数据的结合&#xff0c;不仅为UI设计带来了前所未有的创新机遇&#xff0c;更成为了美学与功能性融合的典范。…

前端 JS 经典:构建工具

前言&#xff1a;相信很多小伙伴在平时听得最多的&#xff0c;就是前端工程化巴拉巴拉什么的&#xff0c;而构建工具就是前端工程化很重要的一环。那么什么是构建工具呐&#xff0c;就是可以对我们的项目进行编译、测试、打包、优化、压缩等功能的工具&#xff0c;称为构建工具…

Zookeeper 面试题(一)

1. ZooKeeper 适合哪些应用场景&#xff1f; ZooKeeper 是一个高性能、高可靠的分布式协调系统&#xff0c;它在分布式系统和大数据领域中有着广泛的应用。以下是 ZooKeeper 适合的一些应用场景&#xff1a; 数据发布/订阅&#xff1a;ZooKeeper 可以作为配置中心&#xff0c;…

C#_初识变量类型与方法

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;namespace ConsoleApp2 {class Program{static void Main(string[] args){///--------常用的变量类型float a 3.12f; //单精度32bit浮点型后缀要加fdou…

使用 HoodieMultiTableStreamer 进行 Debezium CDC 多表同步入湖的研究报告

先介绍一下大的背景吧,我们已经能通过 Flink CDC 将整个数据库同步到 Kafka 中了,这一部分的实现方案已经汇总在了 《Flink CDC 整库 / 多表同步至 Kafka 方案(附源码)》一文中。接下来要完成的是后半程的工作:读取 Kafka 的 Debezium CDC 数据写入到数据湖的 Hudi 表中,…

个人博客网站开发笔记3

文章目录 前言p4 Front Matterp5 配置文件p6 命令p7 部署新的教学视频部署博客到github找视频教程也是一个技能详细步骤安装主题安装渲染器修改主题创建gitub仓库生成密钥验证密钥是否匹配修改config文件推送到github 前言 主要是安装啥的比较费劲 现在已经比较简单了感觉 之…

Android 共享内存

Parcelable 和 Serializable 区别 Serializable IO完成&#xff08;通过磁盘文件读写&#xff09; Parcelable C 对象指针 来实现共享内存 import android.os.Parcel; import androidx.annotation.NonNull;public class ApiResponseBean extends Throwable implements Parce…

基于 Java 的浏览器——JxBrowser使用分享

软件介绍 JxBrowser 是一个基于 Java 的浏览器&#xff0c;它使用 Chromium 引擎来提供高性能的网页渲染和丰富的功能。它支持多种 GUI 框架&#xff0c;如 Swing、JavaFX 和 SWT&#xff0c;使得在 Java 应用程序中嵌入浏览器组件变得简单。 JxBrowser 是一个适用于多种用途…

Django rest_framework 基础应用

1. Django rest_framework示例 以下是一个使用 Django REST framework 构建简单 API 的示例&#xff1a; 模型 首先&#xff0c;我们需要定义一个 Django 模型来表示我们要处理的数据。例如&#xff0c;我们可以定义一个名为 Book 的模型&#xff0c;包含以下字段&#xff1…