当前位置: 首页 > 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;让…...

JavaSec-RCE

简介 RCE(Remote Code Execution)&#xff0c;可以分为:命令注入(Command Injection)、代码注入(Code Injection) 代码注入 1.漏洞场景&#xff1a;Groovy代码注入 Groovy是一种基于JVM的动态语言&#xff0c;语法简洁&#xff0c;支持闭包、动态类型和Java互操作性&#xff0c…...

基于大模型的 UI 自动化系统

基于大模型的 UI 自动化系统 下面是一个完整的 Python 系统,利用大模型实现智能 UI 自动化,结合计算机视觉和自然语言处理技术,实现"看屏操作"的能力。 系统架构设计 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…...

循环冗余码校验CRC码 算法步骤+详细实例计算

通信过程&#xff1a;&#xff08;白话解释&#xff09; 我们将原始待发送的消息称为 M M M&#xff0c;依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)&#xff08;意思就是 G &#xff08; x ) G&#xff08;x) G&#xff08;x) 是已知的&#xff09;&#xff0…...

Qt Widget类解析与代码注释

#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }//解释这串代码&#xff0c;写上注释 当然可以&#xff01;这段代码是 Qt …...

376. Wiggle Subsequence

376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...

【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)

升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点&#xff0c;但无自动故障转移能力&#xff0c;Master宕机后需人工切换&#xff0c;期间消息可能无法读取。Slave仅存储数据&#xff0c;无法主动升级为Master响应请求&#xff…...

OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别

OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别 直接训练提示词嵌入向量的核心区别 您提到的代码: prompt_embedding = initial_embedding.clone().requires_grad_(True) optimizer = torch.optim.Adam([prompt_embedding...

C++ Visual Studio 2017厂商给的源码没有.sln文件 易兆微芯片下载工具加开机动画下载。

1.先用Visual Studio 2017打开Yichip YC31xx loader.vcxproj&#xff0c;再用Visual Studio 2022打开。再保侟就有.sln文件了。 易兆微芯片下载工具加开机动画下载 ExtraDownloadFile1Info.\logo.bin|0|0|10D2000|0 MFC应用兼容CMD 在BOOL CYichipYC31xxloaderDlg::OnIni…...

让回归模型不再被异常值“带跑偏“,MSE和Cauchy损失函数在噪声数据环境下的实战对比

在机器学习的回归分析中&#xff0c;损失函数的选择对模型性能具有决定性影响。均方误差&#xff08;MSE&#xff09;作为经典的损失函数&#xff0c;在处理干净数据时表现优异&#xff0c;但在面对包含异常值的噪声数据时&#xff0c;其对大误差的二次惩罚机制往往导致模型参数…...

初探Service服务发现机制

1.Service简介 Service是将运行在一组Pod上的应用程序发布为网络服务的抽象方法。 主要功能&#xff1a;服务发现和负载均衡。 Service类型的包括ClusterIP类型、NodePort类型、LoadBalancer类型、ExternalName类型 2.Endpoints简介 Endpoints是一种Kubernetes资源&#xf…...