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

这篇具有很好参考价值的文章主要介绍了iOS开发-下拉刷新动画依次渐隐渐显Indicator指示器效果。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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

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

一、效果图

iOS开发-下拉刷新动画依次渐隐渐显Indicator指示器效果,移动开发,iphone开发,Objective-c,ios,下拉刷新动画,MJRefresh
iOS开发-下拉刷新动画依次渐隐渐显Indicator指示器效果,移动开发,iphone开发,Objective-c,ios,下拉刷新动画,MJRefresh

二、基础动画

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下拉刷新操作来执行动画效果。

学习记录,每天不停进步。文章来源地址https://www.toymoban.com/news/detail-617534.html

到了这里,关于iOS开发-下拉刷新动画依次渐隐渐显Indicator指示器效果的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • IOS 类似抖音下拉刷新与自定义上拉加载

    IOS 类似抖音下拉刷新与自定义上拉加载 首先考虑在UICollection与拖动手势之间的问题。 解决UICollectionView上添加手势不能触发。 这里使用了子类继承UICollectionView 在View上添加拖动手势 self.startPoint = CGPointZero; self.isInLoading = NO; 处理手势判断 // 给加的手势设置代理, 并实现此协议

    2024年02月09日
    浏览(38)
  • 微信小程序开发---下拉刷新

    目录 一、下拉刷新的定义 二、下拉刷新的启用 三、配置下拉刷新的样式 四、监听页面的下拉刷新事件 五、停止下拉刷新的效果 下拉刷新是移动端的专有名词,指的是通过手指在屏幕上的下拉滑动操作,让页面重新加载数据 启用下拉刷新有两种方式 (1)全局开启。 在ap

    2024年02月09日
    浏览(34)
  • 【HarmonyOS开发】ArkUI实现下拉刷新/上拉加载

     列表下拉刷新、上拉加载更多,不管在web时代还是鸿蒙应用都是一个非常常用的功能,基于ArkUI中TS扩展的声明式开发范式实现一个下拉刷新,上拉加载。 如果数据量过大,可以使用LazyForEach代替ForEach 高阶组件-上拉加载,下拉刷新 https://gitee.com/bingtengaoyu/harmonyos-advanced-com

    2024年01月20日
    浏览(39)
  • flutter开发实战-下拉刷新与上拉加载更多实现

    flutter开发实战-下拉刷新与上拉加载更多实现 在开发中经常遇到列表需要下拉刷新与上拉加载更多,这里使用EasyRefresh,版本是3.3.2+1 EasyRefresh可以在Flutter应用程序上轻松实现下拉刷新和上拉加载。它几乎支持所有Flutter Scrollable小部件。它的功能与安卓的SmartRefreshLayout非常相似

    2024年02月07日
    浏览(43)
  • 【Android开发基础】说说模块设计(下拉刷新、图片查看、布局设计)

    描述:设计一个简单说说功能模块 需求: 1、能够下拉刷新,更新数据 2、一条说说里允许包含多张图片,图片能够放大查看 难度:初级 知识点: 1、Swiperefreshlayout 2、适配器Adapter 3、对话框Dialog 4、文字图标Icon 1、下拉刷新 思路        使用官方提供的解决方案SwipeRefre

    2024年02月08日
    浏览(38)
  • 鸿蒙App开发-网络请求-下拉刷新三方库-底部Tab栏-滚动组件(含源码)

    本文介绍一个基于鸿蒙ArkTS开发的App,是一个包含轮播图、文章列表和 Web 页面等功能的多页面应用。 本文主要内容包括: 一、效果图 首页 详情页    二、内容简介 1.底部Tab栏和两个页面         App底部是一个TabBar,点击TabBar可以切换上面的页面。共包含两个页面,一个

    2024年02月01日
    浏览(51)
  • uniapp开发使用插件z-paging实现页面下拉刷新、上拉加载,分页加载

    在uniapp官网有一个比较好用的插件z-paging,可以实现多条数据滚动显示或者自定义下拉刷新,分页显示......在开发消息页面或者app开发需要大量的页面刷新,页面内容过长,减轻服务器的负担就可以使用此插件,进入app智慧加载部分内容,等到再次需要之后的内容就再次加载

    2024年02月11日
    浏览(51)
  • 微信小程序开发系列(三十二)·如何通过小程序的API实现页面的上拉加载(onReachBottom事件)和下拉刷新(onPullDownRefresh事件)

    目录 1.  上拉加载 2.  下拉刷新         上拉加载是小程序中常见的一种加载方式,当用户滑动页面到底部时,会自动加载更多的内容,以便用户继续浏览小程序中实现上拉加载的方式: ①  在app.json或者page.json中配置距离页面底部距离:onReachBottomDistance;默认50px。 ②

    2024年04月12日
    浏览(50)
  • iOS开发-CABasicAnimation实现小球左右摆动动画效果

    iOS开发-CABasicAnimation实现小球左右摆动动画效果 之前开发中遇到需要实现小球左右摆动动画效果,这里作下记录。 2.1 CABasicAnimation CABasicAnimation基础动画,包括duration、repeatCount、repeatDuration、beginTime、timingFunction、autoreverses、fromValue、toValue、byValue、byValue等属性。 具体可以查

    2024年02月15日
    浏览(38)
  • 微信小程序——页面事件,.启用下拉刷新监听页面的下拉刷新事件,上拉触底事件,停止下拉刷新的效果

    下拉刷新是移动端的专有名词,指的是通过手指在屏幕上的下拉滑动操作,从而重新加载页面数据的行为。 启用下拉刷新有两种方式: a.全局开启下拉刷新 在 app.json 的window 节点中,将 enablePullDownRefresh 设置为 true. b.局部开启下拉刷新 在页面的.json 配置文件中,将 enablePull

    2024年01月25日
    浏览(55)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包