一个类搞定UIScrollView那些事

本文介绍UIScrollView的高级用法,包括下拉刷新、上拉加载更多、回到顶部等功能的实现思路及关键代码片段。针对不同需求,提供了自定义GIF、帧率等配置选项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

UIScrollView可以说是我们在日常编程中使用频率最多、扩展性最好的一个类,根据不同的需求和设计,我们都能玩出花来,当然有一些需求是大部分应用通用的,今天就聊一下以下需求,在一个category中统统搞定:

1下拉刷新:支持下拉过程中GIF逐帧,loading时可自定义帧率

2上拉更多:支持GIF,支持提前加载,滚动到最后能替换图片作为提示

3回到顶部:当滚动多屏之后,往回划时右下角弹出回到顶部按钮

4自定义一个按钮,在回到顶部按钮上面,可自定事件,并且会根据回到顶部按钮的出现或者消失上下移动,有动画过渡

图3

此类适用于任何继承于scrollView的类

下拉刷新、上拉更多

在头文件中我们声明了2个bool的属性,在没有特殊图片要求的情况下,我们只需要对这2个bool进行操作就可以了,分别是 useRefreshHeaderuseLoadMoreFooter,由于是category,我们需要用 ASSOCIATION来为属性添加setget方法,并且在set方法中,我们为其创建一个默认的view,使用默认的文案和默认的gif图片,也就是上图中你们所看到的猫头,当然也可以自定义gif,并且指定其播放的速度,为此,我提供了以下几个接口方法:

//自定义gif下拉刷新

//传入图片名称
- (void)setRefreshProgressImageName:(NSString *)progressImageName   //下滑时的图片
                   LoadingImageName:(NSString *)loadingImageName    //加载时的图片
                         showTitles:(NSArray *)titles               //显示的title
              LoadingImageFrameRate:(NSInteger)frameRate;           //加载动画的帧率

//传入图片对象
- (void)setRefreshProgressImage:(UIImage *)progressImage   //下滑时的图片
                   LoadingImage:(UIImage *)loadingImage    //加载时的图片
                     showTitles:(NSArray *)titles      //显示的title
          LoadingImageFrameRate:(NSInteger)frameRate;   //加载动画的帧率

//自定义gif上拉更多

//传入图片名称
- (void)setLoadMoreProgressImageName:(NSString *)progressImageName  //跟手动画
                   LoadingImageName:(NSString *)loadingImageName    //加载动画
                            showTitles:(NSArray *)titles            //显示的title
                 LoadingImageFrameRate:(NSInteger)frameRate;        //加载动画的帧率

//传入图片对象
- (void)setLoadMoreProgressImage:(UIImage *)progressImage    //跟手动画
                    LoadingImage:(UIImage *)loadingImage     //加载动画
                          showTitles:(NSArray *)titles       //显示的title
               LoadingImageFrameRate:(NSInteger)frameRate;   //加载动画的帧率

 

更为关键的,我们需要用KVO监听以下几个属性:

1.contentOffset

2.contentSize

3.frame

4.contentInset

属性监听

1.监听contentOffset的目的

由于我们使用了category,所以无法通过scrollView的delegate来获取其滚动,和一般的下拉刷新一样,我们在scrollView的头部添加了一个View,所以必须监听contentOffset才能做出行为判断,上拉更多亦是如此,废话不多说,直接上代码:

if([keyPath isEqualToString:@"contentOffset"])
    {        
        if (self.useRefreshHeader && self.refreshHeaderView && [self.refreshHeaderView isKindOfClass:[TMMuiPullView class]] && [self.refreshHeaderView respondsToSelector:@selector(scrollViewDidScroll:)])
        {
            [self.refreshHeaderView scrollViewDidScroll:[[change valueForKey:NSKeyValueChangeNewKey] CGPointValue]];
        }
        
        if (self.useLoadMoreFooter && self.loadMoreFooterView && [self.loadMoreFooterView isKindOfClass:[TMMuiPullView class]] && [self.loadMoreFooterView respondsToSelector:@selector(scrollViewDidScroll:)])
        {
            [self.loadMoreFooterView scrollViewDidScroll:[[change valueForKey:NSKeyValueChangeNewKey] CGPointValue]];
        }
    }

 

我们通过contentOffset的变化模拟出了一个scrollViewDidScroll的方法,并且在refreshHeaderViewloadMoreFooterView中来监听此方法,而这2个view都是TMMuiPullView,所以其实我只需要实现一次,我会在下文中来详细谈这个方法。

2.监听contentSizeframe的目的

由于useRefreshHeaderuseLoadMoreFooter声明之后,无法避免需要改动scrollView的contentSizeframe的值,所以每当这2个值发生变化的时候,我们需要去调整这2个view的位置和布局

3.监听contentInset的目的

ios 7之后,scrollView在一定条件下,系统会调整其contentInset,或者人为的调整了contentInset,为了能让scrollView在各种动作之后依然处于正确的位置上,我们必须监听这个值,并且储存起来。

TMMuiPullView 中的scrollViewDidScroll方法

这个方法可谓是本类中最繁琐的方法,任何一个动作都需要区分是刷新还是更多2个情况来讨论

TMMuiPullViewTypeRefresh

1.当滚动的offset.y是大于0的时候,我们就直接return,因为之后不可能触发下拉刷新的动作,也就没有必要继续往下走了;

2.我们假设拉到触发刷新动作的距离是100%的话,那么在未触发前都会有对应的一个进度,通过这个进度我们去gif中获取处于这 个进度的那一帧图片,并且把他显示到View上,从而达到跟手逐针播放的效果

3.接下来就是状态判断了,在此,我们为其定制了5个状态:

typedef NS_ENUM(NSUInteger, TMMuiPullState)
{
    TMMuiPullStateNone = 0,   //正常状态
    TMMuiPullStateTriggering,
    TMMuiPullStateTriggered,
    TMMuiPullStateLoading,
    TMMuiPullStateCanFinish
};

 

每一个状态都将是成为下一个状态的条件,这让我想到了“密室逃脱”

  • 第一个房间–TMMuiPullStateNone 这个房间里我们通过滚动到偏移量小于固定某个值的时候&手指依旧按着,那么就能拿到下一个门的钥匙。
  • 第二个房间–TMMuiPullStateTriggering 这个房间拿到钥匙的条件是:进度达到100%;手指依旧按着。
  • 第三个房间–TMMuiPullStateTriggered 我们房间的难度也是越来越苛刻的,这个房间需要满足3个条件:1.没有在loading,2.手指放开,3.放开手指的瞬间,进度是大于95%的。当满足这些条件的时候,播放loading动画,调用delegate方法,然后恭喜你,进入下一个房间
  • 第四个房间–TMMuiPullStateLoading 奇怪,这个房间竟然没有任何提示,也不用做任何操作,怎么回事?需要进入下一个房间的钥匙其实需要delegate塞进来,否则,你永远没办法进入下一个房间,所以在用的时候,我们需要在delegate方法调用loading,loading完成之后调用一个方法把新的房间钥匙送进来。
  • 第五个房间TMMuiPullStateCanFinish 这个最后一个房间,我们就等着进度恢复成初始状态,那么就能顺利的完成这一次密室逃脱,状态置为TMMuiPullStateNone
TMMuiPullViewTypeLoadMore

1.当hasMorePage等于yes的时候,我们用的是loading图片,而当hasMorePage等于no时,我们就用没有更多的图片。 2.其余部分其实和TMMuiPullViewTypeRefresh是同一个道理,只是关键性的值发生了改变这里就不在重复展开了。

回到顶部

1.当设置ShowBackTopButton为Yes时,我们会为scrollView添加一个contentOffset的监听

2.创建一个回到顶部的button,并且添加到与整个scrollView的superView上,并且在屏幕下方隐藏。

3.contentOffset 偏移量大于2屏,且往回滚动时,回到顶部按钮向上做动画上升,否则动画下落

4.当点击回到顶部按钮时,scrollView就调用setContentOffset的方法,滚回顶部

滚回顶部上方的自定义按钮

1.在滚回顶部上方可以添加一个按钮,位于整个试图的右下角,当滚回顶部按钮出现时,它也会随之上升,相反会滚回原来的位置。

注意点

在这个类中我们需要有2个注意点:

1.KVO 不要多次添加,当多次添加KVO时就会有多个监听者监听同一个事件,所以我在每次添加监听的时候都会try着删除一次,记住,一定要写try,否则会crash。

2.runtime交换方法,因为我有许多指针一直持有内存,如果不把这个指针置为nil,也会导致crash,但是我们知道category是没办法重写方法的,所以我们只能用method_exchangeImplementations的方法来做交换,这里我交换了2个地方一个是dealloc,另一个是didMoveToSuperview,因为我们有一些view是放在superView上面的。

总结

虽然本篇博客并没有提及任何实现的具体代码,而是提供一种思路,希望通过了解这些思路,能构建出一个属于你自己的scrollView的category,如果真的有需要,我会代码脱敏之后分享。

转载于:https://www.cnblogs.com/fengmin/p/5518703.html

资源下载链接为: https://pan.quark.cn/s/22ca96b7bd39 在当今的软件开发领域,自动化构建与发布是提升开发效率和项目质量的关键环节。Jenkins Pipeline作为一种强大的自动化工具,能够有效助力Java项目的快速构建、测试及部署。本文将详细介绍如何利用Jenkins Pipeline实现Java项目的自动化构建与发布。 Jenkins Pipeline简介 Jenkins Pipeline是运行在Jenkins上的一套工作流框架,它将原本分散在单个或多个节点上独立运行的任务串联起来,实现复杂流程的编排与可视化。它是Jenkins 2.X的核心特性之一,推动了Jenkins从持续集成(CI)向持续交付(CD)及DevOps的转变。 创建Pipeline项目 要使用Jenkins Pipeline自动化构建发布Java项目,首先需要创建Pipeline项目。具体步骤如下: 登录Jenkins,点击“新建项”,选择“Pipeline”。 输入项目名称和描述,点击“确定”。 在Pipeline脚本中定义项目字典、发版脚本和预发布脚本。 编写Pipeline脚本 Pipeline脚本是Jenkins Pipeline的核心,用于定义自动化构建和发布的流程。以下是一个简单的Pipeline脚本示例: 在上述脚本中,定义了四个阶段:Checkout、Build、Push package和Deploy/Rollback。每个阶段都可以根据实际需求进行配置和调整。 通过Jenkins Pipeline自动化构建发布Java项目,可以显著提升开发效率和项目质量。借助Pipeline,我们能够轻松实现自动化构建、测试和部署,从而提高项目的整体质量和可靠性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值