当前位置: 首页 > news >正文

Swift手撸轮播效果

一、创建ScrollView

@objcMembers class LSLottieAnimView: UIView, UIScrollViewDelegate {private var scrollView: UIScrollView = UIScrollView()func addScrollView() {scrollView.showsHorizontalScrollIndicator = falsescrollView.isPagingEnabled = true// 自定义framescrollView.frame = CGRect(x: 0, y: 0, width: 100, height: 100)scrollView.delegate = self// 此处创建了三个子view的宽度scrollView.contentSize = CGSize(width: scrollView.frame.width * CGFloat(3), height: scrollView.frame.height)let u1 = UIView()u1.frame = scrollView.boundsu1.backgroundColor = .blacklet u2 = UIView()u2.frame = scrollView.boundsu2.backgroundColor = .greenlet u3 = UIView()u3.frame = scrollView.boundsu3.backgroundColor = .yellowscrollView.addSubview(u1)scrollView.addSubview(u2)scrollView.addSubview(u3)UIApplication.shared.keyWindow?.rootViewController?.view.addSubview(scrollView)// 添加拖拽手势识别器let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:)))scrollView.addGestureRecognizer(panGesture)}
}

二、处理手势

@objc func handlePan(_ gesture: UIPanGestureRecognizer) {let scrollView = gesture.view as! UIScrollViewlet translation = gesture.translation(in: scrollView)// let count = scrollView.subviews.count - 1 - 1;// 禁止回弹if (scrollView.contentOffset.x <= 0 && translation.x > 0) || (Int(scrollView.frame.width) * count <= Int(scrollView.contentOffset.x) && translation.x < 0) {return}// 当setContentOffset的动画未完成时,此时isDecelerating为trueif scrollView.isDecelerating || !scrollView.isScrollEnabled {return}switch gesture.state {// case .began:// 如果开启轮播,则停止自动轮播// scrollView.layer.removeAllAnimations()case .changed:// 根据手势的偏移量进行滚动if scrollView.contentOffset.x - translation.x <= 0 {scrollView.contentOffset.x = 0} else if scrollView.contentOffset.x - translation.x >= scrollView.frame.width * CGFloat(count) {scrollView.contentOffset.x = scrollView.frame.width * CGFloat(count)} else {scrollView.contentOffset.x -= translation.x}case .ended, .cancelled, .failed:// 获取滚动视图当前的页索引let pageIndex = Int(scrollView.contentOffset.x / scrollView.frame.width)// 修改后的页面索引var willPageIndex = pageIndex// 根据手势的速度和偏移量来判断是否切换到上一页或下一页let velocity = gesture.velocity(in: scrollView)if velocity.x > 0 {// 向右滑动} else if velocity.x < 0 {// 向左滑动if pageIndex < count {willPageIndex = pageIndex + 1}} else {// 根据偏移量判断是否切换到上一页或下一页if translation.x < -scrollView.frame.width / 2 && pageIndex < count {willPageIndex = pageIndex + 1} else if translation.x > scrollView.frame.width / 2 && pageIndex > 0 {willPageIndex = pageIndex - 1}}// 只有修改后才需要调用setContentOffset,否则会出现scrollViewDidEndScrollingAnimation不调用的情况if (scrollView.contentOffset.x != CGFloat(willPageIndex) * scrollView.frame.width) {scrollView.setContentOffset(CGPoint(x: CGFloat(willPageIndex) * scrollView.frame.width, y: 0), animated: true)scrollView.isScrollEnabled = false}default:break}// 重置手势的偏移量gesture.setTranslation(.zero, in: scrollView)
}// MARK: UIScrollViewDelegate
func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {scrollView.isScrollEnabled = true
}

三、注意事项

1. 如果需要回弹,只需要修改如下代码即可
// 注释如下代码
if (scrollView.contentOffset.x <= 0 && translation.x > 0) || (Int(scrollView.frame.width) * count <= Int(scrollView.contentOffset.x) && translation.x < 0) {return
}// 修改成如下代码case .changed:
//            // 根据手势的偏移量进行滚动
//            if scrollView.contentOffset.x - translation.x <= 0 {
//                scrollView.contentOffset.x = 0
//            } else if scrollView.contentOffset.x - translation.x >= scrollView.frame.width * CGFloat(count) {
//                scrollView.contentOffset.x = scrollView.frame.width * CGFloat(count)
//            } else {scrollView.contentOffset.x -= translation.x
//            }2. 在调用setContentOffset且animated为true时,需要考虑将isScrollEnabled设置为false,等到动画完成后(scrollViewDidEndScrollingAnimation)将isScrollEnabled恢复到true,否则在动画期间仍然可以拖拽
3. 在调用setContentOffset时,如果值和之前相同,则不会触发scrollViewDidEndScrollingAnimation

相关文章:

Swift手撸轮播效果

一、创建ScrollView objcMembers class LSLottieAnimView: UIView, UIScrollViewDelegate {private var scrollView: UIScrollView UIScrollView()func addScrollView() {scrollView.showsHorizontalScrollIndicator falsescrollView.isPagingEnabled true// 自定义framesc…...

数据分析——业务数据描述

业务数据描述 前言一、数据收集数据信息来源企业内部数据源市场调查数据源公共数据源和第三方数据源 二、公司内部数据客户资料数据销售明细数据营销活动数据 三、市场调查数据观察法提问法实验法 四、公共数据五、第三方数据六、数据预处理七、数据清洗丢弃部分数据补全缺失的…...

【哈希表】Leetcode 14. 最长公共前缀

题目讲解 14. 最长公共前缀 算法讲解 我们使用当前第一个字符串中的与后面的字符串作比较&#xff0c;如果第一个字符串中的字符没有出现在后面的字符串中&#xff0c;我们就直接返回&#xff1b;反之当容器中的所有字符串都遍历完成&#xff0c;说明所有的字符串都在该位置…...

(三)JVM实战——对象的内存布局与执行引擎详解

对象的内存布局 对象的实例化 对象的创建方式 - new的方式 - Class的newInstance():反射的方式 - Construct的newInstance() - clone:实现Cloneable接口,默认浅拷贝 - 使用反序列化&#xff1a;将二进制流转化为内存对象 创建对象的步骤 - 判断对象对应的类是否加载、链接、初…...

微信视频号如何变现呢,视频号涨粉最快方法

今天给大家带来的是视频号分成计划 视频号流量主这个项目&#xff0c;可以说这是目前的一个蓝海赛道&#xff0c;做的人也少&#xff0c;外面开的培训也很少&#xff0c;作为副业还是比较适合个人的&#xff0c;如果想批量操作这个项目&#xff0c;也比较适合工作室的。而且这…...

数智先锋 | 多场景数据治理案例,释放数据要素生产力

数据作为第五大生产要素&#xff0c;成为释放新质生产力的关键基础。 当前各个行业数字化建设如火如荼&#xff0c;全力挖掘数据价值以驱动行业高质量应用发展。数据治理成为数据要素价值发挥的重要基础和前提。 数据治理不单是技术问题&#xff0c;不是依赖工具就能解决的&a…...

UE5 audio capture 回声问题 ||在安卓上有爆鸣声

参考视频 0.基本步骤 【UE4_蓝图】录制麦克风声音/系统声音并输出保存WAV文件_ue4录音-CSDN博客 1.步骤 1.创建Sound Submix A 2. 右键新建Sound Submix B 3.把B的两个参数调为-96 4.audio capture的Base Submix&#xff0c;把前面提到的A赋值进去 5.开始录制输出和完成录制…...

第 10 场蓝桥杯小白入门赛题解

1.五一礼物【算法赛】 - 蓝桥云课 (lanqiao.cn) #include <iostream> using namespace std; int main() {cout<<"51"<<endl;return 0; }2.合成贤者之石【算法赛】 - 蓝桥云课 (lanqiao.cn) 假设黄水晶的个数是 x x x,那蓝水晶个数肯定是 x − 1 …...

抖音视频评论区用户采集 根据视频链接批量获取用户信息

要批量获取抖音视频评论区用户的信息&#xff0c;可以通过抖音开放平台的接口来实现。 首先&#xff0c;你需要在抖音开放平台注册开发者账号&#xff0c;并创建一个应用。然后&#xff0c;使用抖音开放平台提供的接口&#xff0c;可以根据视频链接获取视频的详细信息&#xf…...

C++ 多态(一)

一、多态定义 同一种操作作用于不同的对象时&#xff0c;可以产生不同的行为。在面向对象编程中&#xff0c;多态性是指通过继承和重写实现的&#xff0c;同一个方法在不同的子类中可以表现出不同的行为。多态性可以提高代码的灵活性和可扩展性&#xff0c;使得程序更易于维护…...

[Linux][网络][TCP][一][TCP基础][TCP报头]详细讲解

目录 1.TCP头部格式2.TCP协议的特点3.TCP如何封装与分用4.通过序列号和确认应答号提高可靠性1.32位序列号2.32位确认应答号3.保证可靠性4.为什么序列号和确认应答号是单独的字段 5.窗口大小1.TCP的发送和接收缓冲区2.窗口大小 6.连接管理机制 1.TCP头部格式 TCP全称为"传输…...

java-函数式编程-函数对象

定义 什么是合格的函数&#xff1f;无论多少次执行函数&#xff0c;只要输入一样&#xff0c;输出就不会改变 对象方法的简写 其实在类中&#xff0c;我们很多参数中都有一个this&#xff0c;被隐藏传入了 函数也可以作为对象传递&#xff0c;lambda就是很好的例子 函数式接口中…...

致远oa实时获取第三方自定义组件实现

背景 最近的一个需求不好处理&#xff0c;甲方没有准确的数据支撑&#xff0c;各种问题踢皮球&#xff0c;所以有时间实现一个实时获取数据的组件 需求 为了实现方便&#xff0c;封装了一个第三方数据实时展示的组件 只要修改配置&#xff0c;就可以实现字段的删除增加&…...

【Linux-点灯烧录-SD卡/USB烧写】

目录 1. 烧写方式2. 烧写之代码编译2.1 led.s->led.o2.2 led.o->led.elf2.3 led.elf->led.bin2.4 反汇编&#xff1a;led.elf->led.dis 3. 烧写之烧录到SD卡上&#xff1a;3.1 开启烧录软件权限&#xff1a;3.2 确定SD卡的格式&#xff1a;FAT323.3 烧录到SD卡上3.…...

PostgreSQL自带的命令行工具06- pg_isready

PostgreSQL自带的命令行工具06- pg_isready 基础信息 OS版本&#xff1a;Red Hat Enterprise Linux Server release 7.9 (Maipo) DB版本&#xff1a;16.2 pg软件目录&#xff1a;/home/pg16/soft pg数据目录&#xff1a;/home/pg16/data 端口&#xff1a;5777pg_isready 是 Po…...

【请投票】嘉立创EDA中LED发光二极管是否应有统一的引脚定义?

LED发光二极管的引脚定义应该是唯一的吗&#xff1f; 从下面原理图可以看到&#xff0c;器件型号仅尾缀不同,R代表RED红色发光二极管&#xff0c;W代表WHITE指白色发光二极管&#xff0c;是同一家制造商KENTO&#xff0c; 左侧红色发光二极管的1脚是阴极K&#xff0c;2脚是阳极…...

LT6911UXB HDMI2.0 至四端口 MIPI DSI/CSI,带音频 龙迅方案

1. 描述LT6911UXB 是一款高性能 HDMI2.0 至 MIPI DSI/CSI 转换器&#xff0c;适用于 VR、智能手机和显示应用。HDMI2.0 输入支持高达 6Gbps 的数据速率&#xff0c;可为4k60Hz视频提供足够的带宽。此外&#xff0c;数据解密还支持 HDCP2.2。对于 MIPI DSI / CSI 输出&#xff0…...

网络培训议题@2

目录 1. 如何通过IP和掩码确定网关&#xff1a;2. 网络路由的意义和配置方法&#xff1a;3. 网络Bond模式的区别和配置场景&#xff1a;4. 堆叠、VLAN、Trunk、聚合的意义&#xff1a;5. 虚拟机环境下VIP配置和常见问题&#xff1a;6. VXLAN拓展&#xff1a; 1. 如何通过IP和掩…...

linux文本三剑客之sed

目录 1、sed特点 1&#xff09;sed命令格式 2&#xff09;sed命令核心功能"增删改查" 2、sed命令执行过程 3、sed核心应用 1&#xff09;sed-查找p 2&#xff09;sed-删除d 3&#xff09;sed-增加cai 4) sed-替换s 1、sed特点 sed stream editor 流编辑器…...

nginx封禁恶意IP

网络攻击时有发生 TCP洪水攻击、注入攻击、DOS等比较难防的有DDOS等 为了数据安全&#xff0c;防止对手爬虫恶意爬取&#xff0c;封禁IP 一般封禁ip linux server层面封IP&#xff1a;iptablesnginx层面封IP&#xff0c;方式多种&#xff08;但req还是会打进来&#xff0c;让…...

<6>-MySQL表的增删查改

目录 一&#xff0c;create&#xff08;创建表&#xff09; 二&#xff0c;retrieve&#xff08;查询表&#xff09; 1&#xff0c;select列 2&#xff0c;where条件 三&#xff0c;update&#xff08;更新表&#xff09; 四&#xff0c;delete&#xff08;删除表&#xf…...

Admin.Net中的消息通信SignalR解释

定义集线器接口 IOnlineUserHub public interface IOnlineUserHub {/// 在线用户列表Task OnlineUserList(OnlineUserList context);/// 强制下线Task ForceOffline(object context);/// 发布站内消息Task PublicNotice(SysNotice context);/// 接收消息Task ReceiveMessage(…...

电脑插入多块移动硬盘后经常出现卡顿和蓝屏

当电脑在插入多块移动硬盘后频繁出现卡顿和蓝屏问题时&#xff0c;可能涉及硬件资源冲突、驱动兼容性、供电不足或系统设置等多方面原因。以下是逐步排查和解决方案&#xff1a; 1. 检查电源供电问题 问题原因&#xff1a;多块移动硬盘同时运行可能导致USB接口供电不足&#x…...

定时器任务——若依源码分析

分析util包下面的工具类schedule utils&#xff1a; ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类&#xff0c;封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz&#xff0c;先构建任务的 JobD…...

Java 加密常用的各种算法及其选择

在数字化时代&#xff0c;数据安全至关重要&#xff0c;Java 作为广泛应用的编程语言&#xff0c;提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景&#xff0c;有助于开发者在不同的业务需求中做出正确的选择。​ 一、对称加密算法…...

AspectJ 在 Android 中的完整使用指南

一、环境配置&#xff08;Gradle 7.0 适配&#xff09; 1. 项目级 build.gradle // 注意&#xff1a;沪江插件已停更&#xff0c;推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...

Java + Spring Boot + Mybatis 实现批量插入

在 Java 中使用 Spring Boot 和 MyBatis 实现批量插入可以通过以下步骤完成。这里提供两种常用方法&#xff1a;使用 MyBatis 的 <foreach> 标签和批处理模式&#xff08;ExecutorType.BATCH&#xff09;。 方法一&#xff1a;使用 XML 的 <foreach> 标签&#xff…...

前端中slice和splic的区别

1. slice slice 用于从数组中提取一部分元素&#xff0c;返回一个新的数组。 特点&#xff1a; 不修改原数组&#xff1a;slice 不会改变原数组&#xff0c;而是返回一个新的数组。提取数组的部分&#xff1a;slice 会根据指定的开始索引和结束索引提取数组的一部分。不包含…...

图解JavaScript原型:原型链及其分析 | JavaScript图解

​​ 忽略该图的细节&#xff08;如内存地址值没有用二进制&#xff09; 以下是对该图进一步的理解和总结 1. JS 对象概念的辨析 对象是什么&#xff1a;保存在堆中一块区域&#xff0c;同时在栈中有一块区域保存其在堆中的地址&#xff08;也就是我们通常说的该变量指向谁&…...

多元隐函数 偏导公式

我们来推导隐函数 z z ( x , y ) z z(x, y) zz(x,y) 的偏导公式&#xff0c;给定一个隐函数关系&#xff1a; F ( x , y , z ( x , y ) ) 0 F(x, y, z(x, y)) 0 F(x,y,z(x,y))0 &#x1f9e0; 目标&#xff1a; 求 ∂ z ∂ x \frac{\partial z}{\partial x} ∂x∂z​、 …...