封装了一个仿照抖音评论轮播效果的iOS轮播视图

效果图

请添加图片描述

原理

就是我们在一个视图里面有两个子视图,一个是currentView,
一个是willShowView,在一次动画过程中,我们改变current View的frame,同时改变willShowView的frame,同时,需要改变currentVIew 的transform.y不然的话,currentView里面的内容就没有缩放效果了,看起来就是单纯的展示不下的感觉,动画结束之后,将currentView指向willView, willView指向currentView, 同时,将刚刚消失的视图,放到底部,等待下次动画展示

#代码

//
//  RollingCell.m
//  TEXT
//
//  Created by 刘博 on 2021/3/18.
//  Copyright © 2021 刘博. All rights reserved.
//#import "XBNoticeViewCell.h"
#import "XBRollingNoticeView.h"@interface XBRollingNoticeView ()@property (nonatomic, strong) NSMutableDictionary *cellClsDict; //注册 cell 的字典,key为cell的类名,value 为identifier
@property (nonatomic, strong) NSMutableArray *reuseCells; //重用cell的实例对象数组
@property (nonatomic, strong) NSTimer *timer; //计时器
@property (nonatomic, strong) XBNoticeViewCell *currentCell; //当前展示的cell
@property (nonatomic, strong) XBNoticeViewCell *willShowCell; //即将展示的cell
@property (nonatomic, assign) BOOL isAnimating; //动画
@property (nonatomic, assign) BOOL isRefresing ; ///在刷新, 多次刷新的时候,防止上次未执行完的动画对新的一轮刷新造成干扰
///
@property (nonatomic, strong) NSMutableArray *array ;@end@implementation XBRollingNoticeView- (instancetype)initWithFrame:(CGRect)frame
{self = [super initWithFrame:frame];if (self) {[self setupNoticeViews];}return self;
}- (void)setupNoticeViews
{self.clipsToBounds = YES;_stayInterval = 2;_animationDuration = 0.66;_fadeTranslationY = 6;[self addGestureRecognizer:[self createTapGesture]];
}- (void)registerClass:(nonnull Class)cellClass forCellReuseIdentifier:(NSString *)identifier
{[self.cellClsDict setObject:NSStringFromClass(cellClass) forKey:identifier];
}- (__kindof XBNoticeViewCell *)dequeueReusableCellWithIdentifier:(NSString *)identifier
{for (XBNoticeViewCell *cell in self.reuseCells){if ([cell.reuseIdentifier isEqualToString:identifier]) {cell.userInteractionEnabled = NO;return cell;}}Class cellCls = NSClassFromString(self.cellClsDict[identifier]);XBNoticeViewCell *cell = [[cellCls alloc] initWithReuseIdentifier:identifier];cell.userInteractionEnabled = NO;return cell;
}#pragma mark- rolling
- (void)layoutCurrentCellAndWillShowCell
{int count = (int)[self.dataSource numberOfRowsForRollingNoticeView:self];if (_currentIndex > count - 1) {_currentIndex = 0;}int willShowIndex = _currentIndex + 1;if (willShowIndex > count - 1) {willShowIndex = 0;}float w = self.frame.size.width;float h = self.frame.size.height;if (!_currentCell) {// 第一次没有currentcell// currentcell is null at first time_currentCell = [self.dataSource rollingNoticeView:self cellAtIndex:_currentIndex];_currentCell.frame = CGRectMake(0, 0, w, h);if (![self.subviews containsObject:self.currentCell]) {[self addSubview:_currentCell];}if (self.style == RollingStyleDefault) {///默认轮播滚动样式,首次展示不需要加载下一个return;}}CGFloat willY = h + self.spaceOfItem;if (self.style == RollingStyleFade) {//淡入淡出的样式willY = 4;} else if (self.style == RollingStyleScaleY) {willY = h + self.spaceOfItem;}_willShowCell = [self.dataSource rollingNoticeView:self cellAtIndex:willShowIndex];_willShowCell.frame = CGRectMake(0, willY, w, h);if (self.style == RollingStyleFade) {///首次展示currentCell的时候,will 需要隐藏_willShowCell.alpha = 0;}if (![self.subviews containsObject:_willShowCell]) {[self addSubview:_willShowCell];}self.isRefresing = YES;[self.reuseCells removeObject:_currentCell];[self.reuseCells removeObject:_willShowCell];
}- (void)reloadDataAndStartRoll
{[self stopTimer];[self layoutCurrentCellAndWillShowCell];NSInteger count = [self.dataSource numberOfRowsForRollingNoticeView:self];if (count && count < 2) {return;}__weak typeof(self) weakSelf = self;self.timer = [NSTimer timerWithTimeInterval:self.stayInterval + self.animationDuration repeats:YES block:^(NSTimer * _Nonnull timer) {[weakSelf timerHandle];}];NSRunLoop *runLoop = [NSRunLoop currentRunLoop];[runLoop addTimer:self.timer forMode:NSRunLoopCommonModes];
}- (void)stopTimer
{if (_timer) {[_timer invalidate];_timer = nil;}_isAnimating = NO;_currentIndex = 0;[_currentCell removeFromSuperview];[_willShowCell removeFromSuperview];_currentCell = nil;_willShowCell = nil;[self.reuseCells removeAllObjects];
}- (void)pause
{if (_timer) {[_timer setFireDate:[NSDate distantFuture]];}
}- (void)proceed
{if (_timer) {[_timer setFireDate:[NSDate date]];}
}- (void)timerHandle
{if (self.isAnimating) {return;}if (self.style == RollingStyleDefault) {[self defaultTimeHandler];} else if (self.style == RollingStyleFade) {[self fadeTimeHandler];} else if (self.style == RollingStyleScaleY) {[self scaleYTimeHandler];}
}- (void)defaultTimeHandler
{[self layoutCurrentCellAndWillShowCell];_currentIndex++;int count = (int)[self.dataSource numberOfRowsForRollingNoticeView:self];if (_currentIndex > count - 1) {_currentIndex = 0;}float w = self.frame.size.width;float h = self.frame.size.height;self.isAnimating = YES;[UIView animateWithDuration:_animationDuration animations:^{self.currentCell.frame = CGRectMake(0, - h - self.spaceOfItem, w, h);self.willShowCell.frame = CGRectMake(0, 0, w, h);} completion:^(BOOL finished) {// fixed bug: reload data when animate runningif (self.currentCell && self.willShowCell) {[self.reuseCells addObject:self.currentCell];[self.currentCell removeFromSuperview];self.currentCell = self.willShowCell;}self.isAnimating = NO;}];
}- (void)fadeTimeHandler
{self.isRefresing = NO;self.isAnimating = YES;float w = self.frame.size.width;float h = self.frame.size.height;int count = (int)[self.dataSource numberOfRowsForRollingNoticeView:self];int willShowIndex = self->_currentIndex + 1;if (willShowIndex > count - 1) {willShowIndex = 0;}self->_willShowCell = [self.dataSource rollingNoticeView:self cellAtIndex:willShowIndex];self->_willShowCell.frame = CGRectMake(0, self.fadeTranslationY, w, h);self->_willShowCell.alpha = 0;[self addSubview:self.willShowCell];[self.reuseCells removeObject:self.willShowCell];[self.reuseCells removeObject:self.currentCell];///动画隐藏当前的cell[UIView animateWithDuration:self.animationDuration delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{if (self.isRefresing) {self.currentCell.alpha = 1;} else {self.currentCell.alpha = 0;}} completion:^(BOOL finished) {}];[UIView animateWithDuration:self.animationDuration - 0.1 delay:0.1 options:UIViewAnimationOptionCurveLinear animations:^{if (self.isRefresing) {self.currentCell.frame = CGRectMake(0, 0, w, h);} else {self.currentCell.frame = CGRectMake(0, - self.fadeTranslationY, w, h);self.currentCell.alpha = 0;}} completion:^(BOOL finished) {}];///动画展示下一个cell ,/*这里减0.07是需要在上面文案的动画还没有结束的时候,下面文案的动画就要开始了*/dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)((self.animationDuration - 0.07) * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{[self showNext];});
}- (void)showNext
{[UIView animateWithDuration:self.animationDuration animations:^{if (self.isRefresing) {self.willShowCell.alpha = 0;} else {self.willShowCell.alpha = 1;}} completion:^(BOOL finished) {}] ;float w = self.frame.size.width;float h = self.frame.size.height;[UIView animateWithDuration:self.animationDuration - 0.1 delay:0.1 options:UIViewAnimationOptionCurveLinear animations:^{if (self.isRefresing) {self.willShowCell.frame = CGRectMake(0, self.fadeTranslationY, w, h);} else {self.willShowCell.frame = CGRectMake(0, 0, w, h);}} completion:^(BOOL finished) {if (self.isRefresing) {return;}self->_currentIndex++;int count = (int)[self.dataSource numberOfRowsForRollingNoticeView:self];if (self->_currentIndex > count - 1) {self->_currentIndex = 0;}if (self.currentCell && self.willShowCell) {[self.reuseCells addObject:self.currentCell];}self.currentCell = self.willShowCell;self.isAnimating = NO;}];
}- (void)scaleYTimeHandler
{NSInteger count = [self.dataSource numberOfRowsForRollingNoticeView:self];float w = self.frame.size.width;float h = self.frame.size.height;[UIView animateWithDuration:self.animationDuration animations:^{self.currentCell.frame = CGRectMake(0, 0, w, 0);self.currentCell.transform = CGAffineTransformMakeScale(1, 0.01);self.willShowCell.frame = CGRectMake(0, 0, w, h);} completion:^(BOOL finished) {self.currentCell.frame = CGRectMake(0, w + self.spaceOfItem, w, h);self.currentCell.transform = CGAffineTransformMakeScale(1, 1);if (self.willShowCell && self.currentCell) {[self.reuseCells addObject:self.currentCell];}self.currentCell = self.willShowCell;self->_currentIndex += 1;if (self.currentIndex >= count) {self->_currentIndex = 0;}NSInteger willIndex = self.currentIndex + 1;if (willIndex >= count) {willIndex = 0;}self.willShowCell = [self.dataSource rollingNoticeView:self cellAtIndex:willIndex];self.willShowCell.frame = CGRectMake(0, w + self.spaceOfItem, w, h);[self addSubview:self.willShowCell];[self.reuseCells removeObject:self.willShowCell];}];
}#pragma mark - gesture- (void)handleCellTapAction
{int count = (int)[self.dataSource numberOfRowsForRollingNoticeView:self];if (_currentIndex > count - 1) {_currentIndex = 0;}if ([self.delegate respondsToSelector:@selector(didClickRollingNoticeView:forIndex:)]) {[self.delegate didClickRollingNoticeView:self forIndex:_currentIndex];}
}- (UITapGestureRecognizer *)createTapGesture
{return [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(handleCellTapAction)];
}#pragma mark- lazy
- (NSMutableDictionary *)cellClsDict
{if (!_cellClsDict) {_cellClsDict = [[NSMutableDictionary alloc]init];}return _cellClsDict;
}- (NSMutableArray *)reuseCells
{if (!_reuseCells) {_reuseCells = [[NSMutableArray alloc]init];}return _reuseCells;
}- (void)dealloc
{if (self.timer) {[self.timer invalidate];self.timer  = nil;}
}- (NSMutableArray *)array
{if (!_array) {_array = [NSMutableArray array];}return _array;
}@end

如果对您有帮助,欢迎给个star
demo

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

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

相关文章

Linux操作系统:Redis在虚拟环境下的安装与部署

Redis下载方法 最近部署项目的时候用到了Redis&#xff0c;自己在安装的时候也碰到了一些列问题最终安装成功&#xff0c;记录一下自己的安装历程。前期准备&#xff1a; 服务器Linux版本&#xff1a;Centos8.4 64位&#xff08;http://isoredirect.centos.org/centos/8/isos/…

快速了解JVM机制

1.JVM 简介 JVM 是 Java Virtual Machine 的简称&#xff0c;意为 Java虚拟机。 虚拟机是指通过软件模拟的具有完整硬件功能的、运⾏在⼀个完全隔离的环境中的完整计算机系统。 常⻅的虚拟机&#xff1a;JVM、VMwave、Virtual Box。 JVM 和其他两个虚拟机的区别&#xff1a; V…

Ubuntu有线连接消失,无法联网怎么办!

今天重启 Ubuntu 虚拟机&#xff0c;突然之间发现没有网络&#xff0c;打开设置中的网络看&#xff0c;有线网络竟然消失了 经过一番查阅资料&#xff0c;发现解决问题很简单&#xff1a; 先看原因&#xff0c;输入命令 sudo lshw -c Network 检查所有的网络情况&#xff0c…

每天坚持写java锻炼能力---第一天(6.4)

今天的目标是菜单&#xff1a; B站/马士兵的项目菜单 package java1;import java.util.Scanner;public class Test {public static void main(String[] args) {while(true){ //3.加入死循环&#xff0c;让输入一直有System.out.println();System.out.println("--->项…

Python logging 模块详解

Python 的 logging 模块提供了一个强大而灵活的日志系统。它是 Python 标准库的一部分&#xff0c;因此可以在任何 Python 程序中使用。logging 模块提供了许多有用的功能&#xff0c;包括日志消息的级别设置、日志消息的格式设置、将日志消息输出到不同的目标&#xff0c;以及…

Java1.8 vue版家政服务系统成品源码 家政管家系统源码 家政月嫂系统源码 家政保洁系统源码 在线派单,师傅入驻全套商业源码

Java1.8 vue版家政服务系统成品源码 家政管家系统源码 家政月嫂系统源码 家政保洁系统源码 在线派单&#xff0c;师傅入驻全套商业源码 一、系统定义 家政上门服务系统是一种利用互联网技术&#xff0c;将家政服务需求与专业的家政服务人员进行高效匹配的平台。它允许用户通过…

信息系统项目管理师0146:输入(9项目范围管理—9.3规划范围管理—9.3.1输入)

点击查看专栏目录 文章目录 9.3 规划范围管理9.3.1 输入9.3 规划范围管理 规划范围管理是为了记录如何定义、确认和控制项目范围及产品范围,而创建范围管理计划的过程。本过程的主要作用是在整个项目期间对如何管理范围提供指南和方向。本过程仅开展一次或仅在项目的预定义点开…

20240606在Toybrick的TB-RK3588开发板的Android12下确认HDMI的驱动

20240606在Toybrick的TB-RK3588开发板的Android12下确认HDMI的驱动 2024/6/6 9:48 【原文是在RK3328的Android7.1下写的。我将它升级成为RK3588的Android12了】 RK平台主要采用 FB 和 DRM 两种显示框架。与此相对应&#xff0c; HDMI 也有两套驱动。 FB&#xff1a; LINUX 3.10…

ctfshow-web入门-命令执行(web29)五种解法绕过文件名检测

命令执行&#xff0c;需要严格的过滤 进入 php 代码审计了&#xff1a; 第一题代码很简单&#xff0c;就是对 preg_match 绕过&#xff0c;只要提交的参数值不出现 flag 就行 先看一下当前目录下的文件&#xff0c;构造 payload&#xff1a; ?csystem(ls); 可以看到 flag 就…

《魔法与科技的融合:SpringBoot运维的现代传说》

揭开了SpringBoot应用部署的神秘面纱。从云平台的选型到Docker的容器化魔法&#xff0c;再到Kubernetes的集群力量&#xff0c;每一步都充满了奇幻色彩。文章以轻松幽默的笔触&#xff0c;带领读者穿梭于现代应用部署的各个角落&#xff0c;探索自动化部署的奥秘&#xff0c;学…

Spring-Cloud-Gateway--源码分析及应用

文章目录 一、简介1.1 术语 1.3 特性1.4 Spring Cloud Gateway与Spring Cloud ZuulSpring Cloud ZuulWebflux模型 1.5 如何集成Gateway 二、工作原理2.2 Gateway类图 三、配置路由谓词工厂和网关过滤工厂3.1 两种不同的配置路由方式通过yml文件来配置通过Java Bean来配置 3.2 R…

经典的泡泡龙游戏源码免费下载

源码介绍 HTML5泡泡龙冒险小游戏是一款休闲网页游戏&#xff0c;游戏玩法是玩家从下方中央的弹珠发射台射出彩珠&#xff0c;多于3个同色珠相连则会消失。 源码下载 经典的泡泡龙游戏源码免费下载

搜索与图论:有向图的拓扑序列

搜索与图论&#xff1a;有向图的拓扑序列 题目描述参考代码 题目描述 输入样例 3 3 1 2 2 3 1 3输出样例 1 2 3 参考代码 #include <cstring> #include <iostream> #include <algorithm>using namespace std;const int N 100010;int n, m; int h[N], e…

【Qt知识】部分QWidget属性表格

QWidget是Qt库中所有图形用户界面组件的基类&#xff0c;它提供了大量属性以供自定义和配置控件的行为和外观。下面列出了一些主要的QWidget属性及其作用。 属性 作用 accessibleName 控件的辅助技术名称&#xff0c;用于无障碍访问。 accessibleDescription 控件的辅助技…

前端开发之性能优化

本文章 对各大学习技术论坛知识点&#xff0c;进行总结、归纳自用学习&#xff0c;共勉&#x1f64f; 文章目录 1. [CDN](https://www.bootcdn.cn/)2.懒加载3.缓存4.图片压缩5.图片分割6.sprite7.Code Splitting8.gzip9.GPU加速10.Ajax11.Tree Shaking12.Resource Hints 1. CD…

AI大底座核心平台:百度百舸AI异构计算平台(AI IaaS)与AI中台(AI PaaS)

AI大底座正是整合了以上端到端全要素技术能力&#xff0c;将基础架构IaaS与应用平台PaaS能力深度融合&#xff0c;面向企业和产业AI生 产与应用的全生命周期提供完整解决方案。 百舸AI异构计算平台是AI IaaS层的核心平台&#xff0c;包括AI计算、AI存储、AI加速、AI容器四层套件…

Mysql使用中的性能优化——搭建Mysql的监测服务

大纲 环境安装配置Mysql安装设置root密码新增远程访问账户修改绑定地址重启 新增 MySQL Server Exporter 用户 安装启动mysqld_exporter安装启动新增配置启动 安装启动Prometheus创建用户下载并解压修改配置启动 安装启动grafana安装启动 测试参考资料 抛开场景和数据&#xff…

【ARM】PK51-如何添加芯片型号的方法

【更多软件使用问题请点击亿道电子官方网站】 1、 文档目标 遇到打开工程提示没有该芯片设备提示如何解决。 2、 问题场景 客户发来一个工程文件&#xff0c;打开后软件提示没有发现该芯片设备提示。 图 1 3、软硬件环境 1&#xff09;、软件版本&#xff1a;keil μvision…

搜索与图论:八皇后问题

搜索与图论&#xff1a;八皇后问题 题目描述参考代码 题目描述 输入样例 4输出样例 .Q.. ...Q Q... ..Q...Q. Q... ...Q .Q..参考代码 #include <iostream>using namespace std;const int N 20;int n; char g[N][N]; bool col[N], dg[N], udg[N];void dfs(int u) {//…

【机器学习】机器学习与大模型在人工智能领域的融合应用与性能优化新探索

文章目录 引言机器学习与大模型的基本概念机器学习概述监督学习无监督学习强化学习 大模型概述GPT-3BERTResNetTransformer 机器学习与大模型的融合应用自然语言处理文本生成文本分类机器翻译 图像识别自动驾驶医学影像分析 语音识别智能助手语音转文字 大模型性能优化的新探索…