iOS开发-下拉刷新动画依次渐隐渐显Indicator指示器效果

news2024/9/24 15:20:26

iOS开发-下拉刷新动画依次渐隐渐显Indicator指示器效果

之前开发中实现下拉刷新动画三个球依次渐隐渐显指示器效果。

一、效果图

在这里插入图片描述
在这里插入图片描述

二、基础动画

CABasicAnimation类的使用方式就是基本的关键帧动画。

所谓关键帧动画,就是将Layer的属性作为KeyPath来注册,指定动画的起始帧和结束帧,然后自动计算和实现中间的过渡动画的一种动画方式。

可以查看

https://blog.csdn.net/gloryFlow/article/details/131991202

三、实现代码

3.1 代码实现动画

主要三个球实现CABasicAnimation动画,KeyPath是opacity

- (CAAnimation *)fadeInAnimation:(CFTimeInterval)delay {
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"opacity"];
    animation.fromValue = @(0.2f);
    animation.toValue = @(1.0f);
    animation.duration = 0.25f + delay;
    animation.beginTime = CACurrentMediaTime() + delay;
    animation.autoreverses = YES;
    animation.repeatCount = HUGE_VAL;
    return animation;
}

- (CAAnimation *)scaleFadeInAnimation:(CFTimeInterval)delay {
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.scale.xy"];
    animation.fromValue = @(1.0f);
    animation.toValue = @(0.5f);
    animation.duration = 0.25f;
    animation.beginTime = CACurrentMediaTime() + delay;
    animation.autoreverses = YES;
    animation.repeatCount = HUGE_VAL;
    return animation;
}

完整代码如下

#import "INRefreshIndicatorView.h"
#import "UIColor+Addition.h"
#import "UIImageView+WebCache.h"

static CGFloat kBallSize = 12.0;
static CGFloat kDistanceCenter = 25.0;

@interface INRefreshIndicatorView ()

@property (nonatomic, strong) UIImageView *contentBGImageView;
@property (nonatomic, strong) UIImageView *aballImageView;
@property (nonatomic, strong) UIImageView *bballImageView;
@property (nonatomic, strong) UIImageView *cballImageView;
@property (nonatomic, assign) BOOL animating;

@end

@implementation INRefreshIndicatorView

- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        self.hidesWhenStopped = NO;

        [self addSubview:self.contentBGImageView];
        [self.contentBGImageView addSubview:self.aballImageView];
        [self.contentBGImageView addSubview:self.bballImageView];
        [self.contentBGImageView addSubview:self.cballImageView];
        [self layoutBallFrame];
    }
    return self;
}

- (void)layoutBallFrame {
    self.aballImageView.center = self.contentBGImageView.center;
    self.bballImageView.center = self.contentBGImageView.center;
    self.cballImageView.center = self.contentBGImageView.center;
}

#pragma mark - Display
- (void)displayIndicator:(CGFloat)precent {
    if (precent > 1.0) {
        precent = 1.0;
    }
    
    if (precent < 0.0) {
        precent = 0.0;
    }
    
    CGPoint cBallCenter = self.cballImageView.center;
    CGPoint aBallCenter = self.aballImageView.center;
    CGPoint bBallCenter = self.bballImageView.center;
    
    aBallCenter.x = cBallCenter.x - precent*kDistanceCenter;
    self.aballImageView.center = aBallCenter;
    self.aballImageView.transform = CGAffineTransformMakeScale(precent, precent);

    bBallCenter.x = cBallCenter.x + precent*kDistanceCenter;
    self.bballImageView.center = bBallCenter;
    self.bballImageView.transform = CGAffineTransformMakeScale(precent, precent);
}

- (void)startAnimation {
    if (_animating) {
        return;
    }
    
    [self.aballImageView.layer addAnimation:[self scaleFadeInAnimation:0] forKey:@"fadeIn"];
    [self.cballImageView.layer addAnimation:[self scaleFadeInAnimation:0.1] forKey:@"fadeIn"];
    [self.bballImageView.layer addAnimation:[self scaleFadeInAnimation:0.2] forKey:@"fadeIn"];

    self.animating = YES;
}

- (void)stopAnimation {
    if (!_animating) {
        return;
    }
    
    self.animating = NO;
    
    [self.aballImageView.layer removeAnimationForKey:@"fadeIn"];
    [self.bballImageView.layer removeAnimationForKey:@"fadeIn"];
    [self.cballImageView.layer removeAnimationForKey:@"fadeIn"];
}

- (CAAnimation *)fadeInAnimation:(CFTimeInterval)delay {
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"opacity"];
    animation.fromValue = @(0.2f);
    animation.toValue = @(1.0f);
    animation.duration = 0.25f + delay;
    animation.beginTime = CACurrentMediaTime() + delay;
    animation.autoreverses = YES;
    animation.repeatCount = HUGE_VAL;
    return animation;
}

- (CAAnimation *)scaleFadeInAnimation:(CFTimeInterval)delay {
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.scale.xy"];
    animation.fromValue = @(1.0f);
    animation.toValue = @(0.5f);
    animation.duration = 0.25f;
    animation.beginTime = CACurrentMediaTime() + delay;
    animation.autoreverses = YES;
    animation.repeatCount = HUGE_VAL;
    return animation;
}

#pragma mark - SETTER/GETTER
- (UIImageView *)contentBGImageView {
    if (!_contentBGImageView) {
        _contentBGImageView = [[UIImageView alloc] initWithFrame:self.bounds];
        _contentBGImageView.clipsToBounds = YES;
        _contentBGImageView.userInteractionEnabled = YES;
        _contentBGImageView.backgroundColor = [UIColor clearColor];
    }
    return _contentBGImageView;
}

- (UIImageView *)aballImageView {
    if (!_aballImageView) {
        _aballImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0.0, 0.0, kBallSize, kBallSize)];
        _aballImageView.clipsToBounds = YES;
        _aballImageView.userInteractionEnabled = YES;
        _aballImageView.backgroundColor = [UIColor colorWithHexString:@"ff7e48"];
        _aballImageView.layer.cornerRadius = kBallSize/2;
        _aballImageView.layer.masksToBounds = YES;
    }
    return _aballImageView;
}

- (UIImageView *)bballImageView {
    if (!_bballImageView) {
        _bballImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0.0, 0.0, kBallSize, kBallSize)];
        _bballImageView.clipsToBounds = YES;
        _bballImageView.userInteractionEnabled = YES;
        _bballImageView.backgroundColor = [UIColor colorWithHexString:@"ff7e48"];
        _bballImageView.layer.cornerRadius = kBallSize/2;
        _bballImageView.layer.masksToBounds = YES;
    }
    return _bballImageView;
}

- (UIImageView *)cballImageView {
    if (!_cballImageView) {
        _cballImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0.0, 0.0, kBallSize, kBallSize)];
        _cballImageView.clipsToBounds = YES;
        _cballImageView.userInteractionEnabled = YES;
        _cballImageView.backgroundColor = [UIColor colorWithHexString:@"ff7e48"];
        _cballImageView.layer.cornerRadius = kBallSize/2;
        _cballImageView.layer.masksToBounds = YES;
    }
    return _cballImageView;
}

@end

3.2 MJRefresh使用该动画

我这里继承MJRefreshStateHeader

需要根据刷新控件的状态来执行开启动画与结束动画操作

刷新控件的状态如下

typedef NS_ENUM(NSInteger, MJRefreshState) {
    // 普通闲置状态
    MJRefreshStateIdle = 1,
    // 松开就可以进行刷新的状态
    MJRefreshStatePulling,
    // 正在刷新中的状态
    MJRefreshStateRefreshing,
    // 即将刷新的状态
    MJRefreshStateWillRefresh,
    // 所有数据加载完毕,没有更多的数据了
    MJRefreshStateNoMoreData
};

INRefreshHeader.h

#import "MJRefresh.h"
#import "INRefreshFourBallLoading.h"

@interface INRefreshHeader : MJRefreshStateHeader

@property (nonatomic, assign) BOOL showInsetTop;

@property (nonatomic, strong) INRefreshIndicatorView *indicatorView;

@end

INRefreshHeader.m

@implementation INRefreshHeader

- (instancetype)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        self.lastUpdatedTimeLabel.hidden = YES;
        self.stateLabel.hidden = YES;
        
        [self addSubview:self.indicatorView];
    }
    return self;
}

- (INRefreshIndicatorView *)indicatorView {
    if (!_indicatorView) {
        _indicatorView = [[INRefreshIndicatorView alloc] initWithFrame:CGRectMake(0.0, 0.0, CGRectGetWidth([UIScreen mainScreen].bounds), self.bounds.size.height)];
    }
    return _indicatorView;
}


- (void)setState:(MJRefreshState)state {
    MJRefreshCheckState
    
    // 根据状态做事情
    if (state == MJRefreshStateIdle) {
        if (oldState == MJRefreshStateRefreshing) {
            self.indicatorView.alpha = 1.0;

            // 如果执行完动画发现不是idle状态,就直接返回,进入其他状态
            if (self.state != MJRefreshStateIdle) return;
            
            self.indicatorView.alpha = 1.0;

            [self.indicatorView stopAnimation];

        } else {
            [self.indicatorView stopAnimation];

        }
    } else if (state == MJRefreshStatePulling) {
        [self.indicatorView stopAnimation];

    } else if (state == MJRefreshStateRefreshing) {
        self.indicatorView.alpha = 1.0;
    }
}

- (void)prepare {
    [super prepare];
    self.mj_h = 60.0;
}

- (void)placeSubviews {
    [super placeSubviews];
    
    CGFloat centerX = self.mj_w * 0.5;
    CGFloat centerY = self.mj_h * 0.5;
    self.indicatorView.center = CGPointMake(centerX, centerY);
}

/** 当scrollView的contentOffset发生改变的时候调用 */
- (void)scrollViewContentOffsetDidChange:(NSDictionary *)change {
    [super scrollViewContentOffsetDidChange:change];
    NSLog(@"change:%@",change);
    
    CGPoint old = [change[@"old"] CGPointValue];
    CGPoint new = [change[@"new"] CGPointValue];
    
    CGFloat precent = -new.y/self.mj_h;
    [self.indicatorView displayIndicator:precent];
}

/** 当scrollView的contentSize发生改变的时候调用 */
- (void)scrollViewContentSizeDidChange:(NSDictionary *)change {
    [super scrollViewContentSizeDidChange:change];
}

/** 当scrollView的拖拽状态发生改变的时候调用 */
- (void)scrollViewPanStateDidChange:(NSDictionary *)change {
    [super scrollViewPanStateDidChange:change];
}

- (void)setShowInsetTop:(BOOL)showInsetTop {
    _showInsetTop = showInsetTop;
    
}

- (void)backInitState {
    
}

@end

3.3 具体的TableView使用

需要设置UITableView的下拉刷新操作:tableView.mj_header = header

- (void)configureRefresh {
    __weak typeof(self) weakSelf = self;
    INRefreshHeader *header = [INRefreshHeader headerWithRefreshingBlock:^{
        [weakSelf refreshData];
    }];
    
    INRefreshFooter *footer = [INRefreshFooter footerWithRefreshingBlock:^{
        [weakSelf loadMoreData];
    }];
    self.editView.tableView.mj_header = header;
    self.editView.tableView.mj_footer = footer;
}

- (void)refreshData {
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [self.editView.tableView.mj_header endRefreshing];
        [self.editView.tableView.mj_footer endRefreshing];
    });
}

- (void)loadMoreData {
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [self.editView.tableView.mj_header endRefreshing];
        [self.editView.tableView.mj_footer endRefreshing];
    });
}

四、小结

iOS开发-下拉刷新动画依次渐隐渐显Indicator指示器效果,使用mjrefresh一个好用的上下拉刷新的控件。实现CABasicAnimation基础效果,根据不同的mjrefresh下拉刷新操作来执行动画效果。

学习记录,每天不停进步。

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

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

相关文章

【Linux进程】进程控制(下) {进程程序替换:程序替换的工作原理,程序替换函数exec*,简单的命令行解释器}

四、进程程序替换 之前用fork创建子进程后&#xff0c;父子进程执行同一个程序的不同代码段。 如何使子进程执行另一个不同的程序呢&#xff1f;子进程需要进行程序替换&#xff01; 程序替换&#xff0c;就是通过特定的接口&#xff0c;将磁盘上一个全新的程序&#xff08;包…

STL中的神秘“指针”:迭代器

&#x1f680;write in front&#x1f680; &#x1f4dc;所属专栏&#xff1a;C学习 &#x1f6f0;️博客主页&#xff1a;睿睿的博客主页 &#x1f6f0;️代码仓库&#xff1a;&#x1f389;VS2022_C语言仓库 &#x1f3a1;您的点赞、关注、收藏、评论&#xff0c;是对我最大…

【Linux】- RPM 与 YUM

RPM 与 YUM 1.1 rpm 包的管理1.2 rpm 包的简单查询指令1.3 rpm 包的其它查询指令&#xff1a;1.4 卸载 rpm 包&#xff1a;2.1 安装 rpm 包3.1 yum3.2 yum 的基本指令3.3 安装指定的 yum 包3.4 yum 应用实例&#xff1a; 1.1 rpm 包的管理 介绍 rpm 用于互联网下载包的打包及安…

SDN系统方法 | 9. 接入网

随着互联网和数据中心流量的爆炸式增长&#xff0c;SDN已经逐步取代静态路由交换设备成为构建网络的主流方式&#xff0c;本系列是免费电子书《Software-Defined Networks: A Systems Approach》的中文版&#xff0c;完整介绍了SDN的概念、原理、架构和实现方式。原文: Softwar…

【Spring Boot丨序列化、反序列化】

序列化、反序列化 概述Jackson 序列化和反序列化简介自定义序列化器注册外部序列化程序&#xff1a; 指定类的 Json 序列化、反序列化 主页传送门&#xff1a;&#x1f4c0; 传送 概述 序列化是将对象转换为字节序列的过程&#xff0c;而反序列化则是将字节序列恢复为对象的过…

用于系统监控及进程管理python库之psutil

前言 对于一个job级别应用再进行测试的过程中&#xff0c;不可避免测试该服务的一些性能&#xff0c;比如占有cpu的使用量&#xff0c;使用的memory的大小等&#xff0c;比较简单的方式是在服务中起一个并行的线程&#xff0c;每隔一段时间打印这些关注量的大小&#xff0c;之后…

【二分答案】CF1661 C

Problem - C - Codeforces 题意&#xff1a; 思路&#xff1a; 在check的时候&#xff0c;我们要尽量用算贡献的思想&#xff0c;并且大胆贪心 Code&#xff1a; #include <bits/stdc.h>#define int long longusing namespace std;const int mxn3e510; const int mxe3…

MySQL基础扎实——列对比运算符是什么

词义解释 在MySQL中&#xff0c;用于进行列对比的运算符主要有以下几种&#xff0c;其实就是逻辑运算符号&#xff1a; 等号&#xff08;&#xff09;&#xff1a;用于判断两个列是否相等&#xff0c;例如&#xff1a;column_name value。 不等号&#xff08;<>或!&am…

Verilog语法学习——边沿检测

边沿检测 代码 module edge_detection_p(input sys_clk,input sys_rst_n,input signal_in,output edge_detected );//存储上一个时钟周期的输入信号reg signal_in_prev;always (posedge sys_clk or negedge sys_rst_n) beginif(!sys_rst_n)signal_in_prev < 0;else…

95. Python基础教程:异常处理try...except语句

【目录】 文章目录 1. try...except语法解析2. 程序异常3. except的4种使用方式3.1 单独的except3.2 except 异常名称3.3 except 异常类型 as 别名3.4 except (异常类型1,异常类型2) as 别名 4. 总结 【正文】 1. try…except语法解析 try[traɪ]&#xff1a;尝试。 except[…

【QT】Day4

1> 思维导图 2> 手动完成服务器的实现&#xff0c;并具体程序要注释清楚 widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QTcpServer> //服务器类 #include <QTcpSocket> //客户端类 #include <QMessageBox> //…

综合能源系统(4)——综合能源系统建模方法

综合能源系统关键技术与典型案例  何泽家&#xff0c;李德智主编 本文主要从物理、信息、价值三个方面介绍综合能源系统关键技术&#xff0c;如图3-1所示。 物理方面&#xff1a;主要包括综合能源系统建模分析技术、规划设计(配置)技术、优化控制技术、运行维护技术和综合评…

mybatisPlus之逻辑删除解读

目录 为什么会有逻辑删除 逻辑删除基本介绍 逻辑删除的使用 局部使用 全局使用 为什么会有逻辑删除 在我们对数据进行增删查改的时候&#xff0c;对于删除操作来说&#xff0c;我们思考一个问题&#xff0c;在实际开发中我们真的会将数据完成从数据库中删除掉么&#xff1f…

二叉搜索树(二叉排序树)

文章目录 基本概念基本操作实现分析插入数据查找数据删除数据遍历数据 源码 基本概念 二叉搜索树也叫搜索二叉树、二叉排序树、排序二叉树。是一种对查找和排序都有用的特殊二叉树。 二叉搜索树&#xff08;Binary Search Tree&#xff0c;简称BST&#xff09; 如何构建一颗二叉…

【使用维纳滤波进行信号分离】基于维纳-霍普夫方程的信号分离或去噪维纳滤波器估计(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

【Windows】WDS中如何跳过语言选择以及身份验证

WDS&#xff08;Windows Deployment Services&#xff09;是微软的一项网络服务&#xff0c;用于快速和方便地部署Windows操作系统到多台计算机上。它提供了一种自动化的方式来安装、配置和管理操作系统映像&#xff0c;使企业能够快速部署和更新大量的计算机系统。网上有很多W…

二叉搜索树的本质

引言 打算写写树形数据结构&#xff1a;二叉查找树、红黑树、跳表和 B 树。这些数据结构都是为了解决同一个基本问题&#xff1a;如何快速地对一个大集合执行增删改查。 本篇是第一篇&#xff0c;讲讲搜索树的基础&#xff1a;二叉搜索树。 基本问题 如何在一千万个手机号中…

设计模式-中介者模式在Java中使用示例-客户信息管理

场景 欲开发客户信息管理窗口界面&#xff0c;界面组件之间存在较为复杂的交互关系&#xff1a;如果删除一个客户&#xff0c; 要在客户列表(List)中删掉对应的项&#xff0c;客户选择组合框(ComboBox)中客户名称也将减少一个&#xff1b; 如果增加一个客户信息&#xff0c;…

SpringBoot2.7集成Swagger3.0和knife4j实现API接口文档开发

1. 概述 Swagger 3 是一个用于描述、构建和测试 RESTful Web 服务的开源工具集。它提供了一种简单而强大的方式来定义和文档化 API 接口&#xff0c;同时还具备自动生成客户端代码和服务器存根代码的功能。 Knife4j是一个集Swagger2 和 OpenAPI3为一体的增强解决方案&#xff…

DNSPod十问秦勇:为什么医疗AI最爱眼科?

本期嘉宾 秦勇 鹰瞳科技Airdoc COO 秦勇&#xff0c;鹰瞳科技&#xff08;Airdoc&#xff09;COO&#xff0c;中国人工智能学会智慧医疗专委会委员&#xff0c;长期从事医疗人工智能方向的工作&#xff0c;拥有丰厚的项目经验、管理经验和技术积累&#xff0c;参与多个医院人…