iOS开发-自定义下拉刷新控件PullDownRefreshView

news2025/1/12 1:53:53

iOS开发-自定义下拉刷新控件PullDownRefreshView

在开发过程中,有时候需要自定义下拉刷新控件,这时候我们就需要自定义PullDownRefreshView。

PullDownRefreshView是在UIScrollView上。
在这里插入图片描述

一、UIScrollView

在下拉刷新过程中,需要实现UIScrollView的delegate的相关方法

// 拖动过程中

  • (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView;
  • (void)scrollViewDidScroll:(UIScrollView *)scrollView;
  • (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate;

三个方法调用顺序

  • 先执行scrollViewWillBeginDragging:,将要开始拖拽
  • 然后执行n个scrollViewDidScroll,只要是scrollView的contentOffset发生改变就会执行,无论是通过手动拖拽还是代码改变了contentOffset
  • 最后执行scrollViewDidEndDragging:willDecelerate:,拖拽结束,因为没有减速滑动,所以此时也是scrollView最终停止位置

二、实现下拉刷新PullDownRefreshView

在下拉过程中显示,我们需要将refreshView初始化

- (SDPullDownRefreshView *)refreshView {
    if (!_refreshView) {
        _refreshView = [[SDPullDownRefreshView alloc] initWithFrame:CGRectMake(0.0, 0.0, CGRectGetWidth([UIScreen mainScreen].bounds), 80)];
    }
    return _refreshView;
}

配置下拉刷新

- (void)configRefresh {
    [self.refreshView setupWithOwner:self.noticationView.tableView delegate:self];
}

实现UIScrollView的Delegate的相关方法

- (void)pullDownRefreshDidFinish {
    [self performSelector:@selector(stop) withObject:nil afterDelay:1];
}

- (void)stopLoading {
    [self.refreshView stopLoading];
}

- (void)stop {
    [self stopLoading];
}

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
    [self.refreshView scrollViewWillBeginDragging:scrollView];
}

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    [self.refreshView scrollViewDidScroll:scrollView];
}

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
    [self.refreshView scrollViewDidEndDragging:scrollView willDecelerate:decelerate];
}

整体SDPullDownRefreshView的完整代码

SDPullDownRefreshView.h

#import <UIKit/UIKit.h>

@protocol SDPullDownRefreshViewDelegate;

@interface SDPullDownRefreshView : UIView {
    BOOL isDragging;
    BOOL isLoading;
}

@property (nonatomic, weak) id delegate;
@property (nonatomic, weak) UIScrollView *owner;
@property (nonatomic, strong) UILabel *refreshLabel;
@property (nonatomic, strong) UIActivityIndicatorView *refreshSpinner;

- (void)setupWithOwner:(UIScrollView *)owner delegate:(id<SDPullDownRefreshViewDelegate>)delegate;

- (void)startLoading;
- (void)stopLoading;

// 拖动过程中
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView;
- (void)scrollViewDidScroll:(UIScrollView *)scrollView;
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate;

@end

@protocol SDPullDownRefreshViewDelegate <NSObject>

- (void)pullDownRefreshDidFinish;

@end

SDPullDownRefreshView.m

#import "SDPullDownRefreshView.h"

#define REFRESH_PULL_UP_STATUS @"下拉可以回到上面"
#define REFRESH_RELEASED_STATUS @"可以松开了"
// 加载中
#define REFRESH_LOADING_STATUS @""
#define REFRESHER_HEIGHT 50.0f

@interface SDPullDownRefreshView ()

@end

@implementation SDPullDownRefreshView

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        self.backgroundColor = [UIColor clearColor];
        
        self.clipsToBounds = YES;
        
        self.refreshLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, frame.size.width, REFRESHER_HEIGHT)];
        self.refreshLabel.backgroundColor = [UIColor clearColor];
        self.refreshLabel.font = [UIFont boldSystemFontOfSize:12.0];
        self.refreshLabel.textAlignment = NSTextAlignmentCenter;
        
        self.refreshSpinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
        self.refreshSpinner.frame = CGRectMake((frame.size.width - 20)/2, (REFRESHER_HEIGHT - 20)/2, 20, 20);
        self.refreshSpinner.hidesWhenStopped = YES;
        
        [self addSubview:self.refreshLabel];
        [self addSubview:self.refreshSpinner];
    }
    return self;
}

- (void)layout:(CGPoint)offset {
    if (offset.y < 0) {
        [self setFrame:CGRectMake(0, 0 - REFRESHER_HEIGHT, self.frame.size.width, REFRESHER_HEIGHT)];
        self.refreshLabel.frame = CGRectMake(0, 0, self.frame.size.width, REFRESHER_HEIGHT);
        self.refreshSpinner.frame = CGRectMake((self.frame.size.width - 20)/2, (REFRESHER_HEIGHT - 20)/2, 20, 20);

    } else {
        [self setFrame:CGRectMake(0, -REFRESHER_HEIGHT, self.frame.size.width, 0)];
        self.refreshLabel.frame = CGRectMake(0, 0, self.frame.size.width, REFRESHER_HEIGHT);
        self.refreshSpinner.frame = CGRectMake((self.frame.size.width - 20)/2, (REFRESHER_HEIGHT - 20)/2, 20, 20);
    }
}


- (void)setupWithOwner:(UIScrollView *)owner  delegate:(id)delegate {
    self.owner = owner;
    self.delegate = delegate;
    
    [_owner addSubview:self];
}

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
    if (isLoading) {
        return;
    }
    isDragging = YES;
}

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    
    CGPoint offset = scrollView.contentOffset;
    [self layout:offset];
    
    if (isLoading && offset.y > 0) {
        return;
    }
    
    if (isDragging && offset.y <= 0 ) {
        
        [self.refreshSpinner stopAnimating];
        // Update the arrow direction and label
        if (scrollView.contentOffset.y <= -REFRESHER_HEIGHT) {
            // User is scrolling above the header
            self.refreshLabel.text = REFRESH_RELEASED_STATUS;
        } else {
            // User is scrolling somewhere within the header
            self.refreshLabel.text = REFRESH_PULL_UP_STATUS;
        }
    }
}

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
    if (isLoading) return;
    isDragging = NO;
    
    // 上拉刷新
    if(scrollView.contentOffset.y <= -REFRESHER_HEIGHT){
        [self startLoading];
    }
}


- (void)startLoading
{
    if (isLoading) {
        return;
    }
    isLoading = YES;
    
    UIEdgeInsets contentInset = self.owner.contentInset;
    contentInset.top = REFRESHER_HEIGHT;
    self.refreshLabel.text = REFRESH_LOADING_STATUS;

    [UIView animateWithDuration:0.25 animations:^{
        self.owner.contentInset = contentInset;
        [self.refreshSpinner startAnimating];
    } completion:^(BOOL finished) {
        // Refresh action!
        if ([self.delegate respondsToSelector:@selector(pullDownRefreshDidFinish)]) {
            [self.delegate performSelector:@selector(pullDownRefreshDidFinish) withObject:nil];
        }
    }];
}

- (void)stopLoading {
    isLoading = NO;
    
    UIEdgeInsets contentInset = self.owner.contentInset;
    contentInset.top = 0.0;
    [UIView animateWithDuration:0.15 animations:^{
        self.owner.contentInset = contentInset;
    } completion:^(BOOL finished) {
        
        // Reset the header        
        self.refreshLabel.text = REFRESH_PULL_UP_STATUS;
        
        [self setFrame:CGRectMake(0, -REFRESHER_HEIGHT, self.frame.size.width, 0)];
        [self.refreshSpinner stopAnimating];
    }];
}

@end


三、小结

iOS开发-自定义下拉刷新控件PullDownRefreshView

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

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

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

相关文章

天锐绿盾企业内网安全管理软件

天锐绿盾&#xff0c;又名绿盾信息安全管理软件是一款企业内网安全管理软件&#xff0c;它将局域网内文件的透明加密、内网的有效管理有效地结合起来&#xff0c;功能强大&#xff0c;能满足不同类型企业用户对信息安全的需求。 试用下载&#xff1a;www.drhchina.com 主要功能…

深度学习与计算机视觉(一)

文章目录 计算机视觉与图像处理的区别人工神经元感知机 - 分类任务Sigmoid神经元/对数几率回归对数损失/交叉熵损失函数梯度下降法- 极小化对数损失函数线性神经元/线性回归均方差损失函数-线性回归常用损失函数使用梯度下降法训练线性回归模型线性分类器多分类器的决策面 soft…

2016年亚太杯APMCM数学建模大赛A题基于光学信息数据的温度及关键元素含量预测求解全过程文档及程序

2016年亚太杯APMCM数学建模大赛 A题 基于光学信息数据的温度及关键元素含量预测 原题再现 光含有能量&#xff0c;在一定条件下可以转化为热。燃烧是一种常见的现象&#xff0c;既能发光又能发热。光和热通常是同时存在的&#xff0c;一般来说&#xff0c;光强度越高&#xf…

一招教会跨国企业如何解决跨境数据传输的丢包和延迟问题

跨境数据传输是跨国企业的重要需求&#xff0c;无论是进行远程协作、视频会议、在线教育、电子商务、游戏娱乐等业务&#xff0c;都需要实时地在不同国家和地区之间传输数据。然而&#xff0c;跨境数据传输也面临着很多挑战&#xff0c;如网络延迟、丢包、不稳定等&#xff0c;…

I/O软件层次结构(用户层软件,设备独立性软件,设备驱动程序,中断处理程序,硬件)

文章目录 1.用户层软件2.设备独立性软件1.主要实现的功能2.逻辑设备表&#xff08;LUT&#xff09; 3.设备驱动程序4.中断处理程序2.中断处理程序的处理流程 4.硬件 1.用户层软件 用户层软件实现了与用户交互的接口&#xff0c;用户可直接使用该层提供的、与I/O操作相关的库函数…

Go 语言代码断行规则详解

一、Go代码断行概念 代码断行&#xff0c;或称为代码换行&#xff0c;是编程中一个很常见但却经常被忽视的细节。它涉及到如何在代码行结束时添加换行符&#xff0c;使代码展示为多行结构。代码断行不仅影响代码的可读性和美观性&#xff0c;还可能与编程语言的语法、编译器行…

Framework -- 系统架构

一、前言 framework的学习&#xff0c;需要掌握到什么程度&#xff1f; App 的启动流程&#xff1a;整体的过程&#xff0c;具体到某些类在整个流程中所起的作用&#xff1b;组件的设计模式&#xff0c;核心设计思想&#xff1b;需要知晓目前已知的问题&#xff0c;以及解决方…

定档11月2日,YashanDB 2023年度发布会完整议程公布

数据库作为支撑核心业务的关键技术&#xff0c;对数字经济的发展有着重要的支撑作用&#xff0c;随着云计算、AI等技术的迅猛发展和数据量的爆发式增长&#xff0c;推动着数据库技术的加速创新。 为了应对用户日益复杂的数据管理需求&#xff0c;赋能行业国产化建设和数字化转型…

python 线程池/AIO(异步非阻塞)调用接口示例

分别测试了 多线程、线程池、aio 等模式 线程受CPU调度限制&#xff0c;大请求量下 AIO 效率最高&#xff0c;详见代码 注释中有详细说明&#xff0c;main 方法中是程序入口 """ python 各种方式发起 http 请求对比 参考&#xff1a; https://plainenglish.io…

“第五十一天”

无符号整数&#xff1a; 计算机硬件在进行无符号整数的加法时&#xff0c;从最低位开始&#xff0c;按位相加&#xff0c;并往更高位进位。 当进行减法时&#xff0c;被减数不变&#xff0c;减数全部按位取反&#xff0c;末位加一&#xff08;将一个正数变负&#xff0c;或者…

关键词搜索淘宝商品数据接口(标题|主图|SKU|价格|优惠价|掌柜昵称|店铺链接|店铺所在地)

关键词搜索淘宝商品数据接口是可以通过API的方式来进行调用。这些接口可以获取到商品列表的标题、SKU ID、价格、优惠价、收藏数、月销售量等数据。 通过这些接口&#xff0c;可以实现关键词搜索淘宝商品列表的功能&#xff0c;也可以获取到商品详情页的数据信息&#xff0c;适…

微信自动通过好友请求是怎么设置的?

微信是一款必不可少的社交工具&#xff0c;尤其对于需要使用微信进行业务沟通和促进成交的人来说。 业务繁忙时可能每天有几十上百个微信好友申请&#xff0c;这时候如果手动“通过验证”不仅会工作质量和效率&#xff0c;有可能由于通过不及时而导致客户流失。 方式一 自动通…

leetCode 76. 最小覆盖子串 + 滑动窗口

76. 最小覆盖子串 - 力扣&#xff08;LeetCode&#xff09; 给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串&#xff0c;则返回空字符串 "" 注意&#xff1a; 对于 t 中重复字符&#xff0c;我们寻…

D71X-16Q手柄蝶阀型号解析

D71X-16Q型号字母含义解析 D71X-16Q是德特森阀门常用的手柄蝶阀型号字母分别代表的意思是: D——代表阀门类型《蝶阀》 7——代表连接方式《对夹》 1——代表结构形式《中线》 X——代表阀座材质《橡胶》 -代表分隔键 16——代表公称压力《1.6MPA》 Q——代表阀体材料《…

FPGA设计时序约束七、设置时钟不确定约束

一、背景 在之前的时序分析中&#xff0c;通常是假定时钟是稳定理想的&#xff0c;即设置主时钟周期后按照周期精确的进行边沿跳动。在实际中&#xff0c;时钟是非理想存在较多不确定的影响&#xff0c;存在时延和波形的变化&#xff0c;要准确分析时序也需将其考虑进来&#x…

2023深耕kotlin,谈谈前景

为什么学习kotlin&#xff1f; Kotlin 早就已经是 Google 官方推荐的开发语言了&#xff0c;而且 Android 新的 Compose 框架只支持 Kotlin &#xff0c;在 Google 那里&#xff0c;Android开发中 Java 其实已经被淘汰了。Java 和 Kotlin 虽然都属于高级语言&#xff0c;但是 …

利用nicegui开发ai工具示例

from fastapi import FastAPI import uvicorn from nicegui import uiclass PipRequirement:def __init__(self):ui.label("依赖安装与依赖展示")class BasicSettings:def __init__(self):self.project_select ui.select(["test"], label"项目选择&q…

驱动获取设备树节点信息

mycdev.c #include <linux/init.h> #include <linux/module.h> #include <linux/of.h>struct device_node *dnode; //解析得到的设备树节点对象指针 struct property *pr; unsigned int lenth; static int __init mycdev_init(void) {//解析设备树节点信息d…

Hafnium安全分区管理器和示例参考软件栈

安全之安全(security)博客目录导读 目录 一、安全分区管理器 1、术语 2、对旧平台的支持 二、示例参考软件栈 一、安全分区管理器 安全分区管理器的三种实现在TF-A代码库并存&#xff1a; 1.基于FF-A规范的S-EL2 SPMC&#xff08;SPM Core&#xff09;&#xff0c;使能安全…

C++ 模板和泛型编程详解

C中的模板和泛型编程是非常重要的概念。模板是一种将数据类型作为参数的通用程序设计方法。它们允许开发人员编写可以处理各种数据类型的代码&#xff0c;而无需为每种数据类型编写不同的代码。下面介绍了一些关于C中模板和泛型编程的重要知识点 模板的定义 模板是一种通用程序…