iOS自定义下拉刷新控件
自定义下拉刷新控件
概述
用了很多的别人的下拉刷新控件,想写一个玩玩,自定义一个在使用的时候也会比较有意思。使应用更加的灵动一些,毕竟谁不喜欢各种动画恰到好处的应用呢。
使用方式如下:
tableview.refreshControl = XRefreshControl.init(refreshingBlock: {DispatchQueue.main.asyncAfter(deadline: .now() + 5) { [weak self] inself?.table.endRefreshing()}
})
下边展示一下效果。

然后又搞了一个比较炫酷的版本~,效果图如下:

继承 UIRefreshControl,然后再其上直接添加view就能实现需要的加载效果,尝试发现自定义的类需要把背景色设置一下,要不然会有一下拉整体都显示出来的问题,而且最好在view上再加一个view整体给铺上,在设置一个背景色,把小菊花给盖上。
简单版本代码
import Foundation
import UIKit
import SnapKitclass XRefreshControl: UIRefreshControl {var observation: NSKeyValueObservation?var isLocalRefreshing: Bool = falselet indicator = UIProgressView(progressViewStyle: .bar)var refreshingBlock: (()->Void)?override init(frame: CGRect) {super.init(frame: frame)observation = observe(\.frame,options: .new) { [weak self] object, change inif self?.isRefreshing == true {if self?.isLocalRefreshing == false {if self?.refreshingBlock != nil {self?.refreshingBlock!()}}self?.isLocalRefreshing = true} else {let height = change.newValue!.heightself?.indicator.progress = min(Float(abs(height / 60)), 1)}}}convenience init(refreshingBlock: @escaping ()->Void) {self.init(frame: .zero)self.refreshingBlock = refreshingBlockself.layer.masksToBounds = trueself.backgroundColor = .redlet v = UIView()v.backgroundColor = .redlet center = UIView()v.addSubview(center)let title = UILabel()title.text = "加载中"title.textColor = .blackcenter.addSubview(title)indicator.layer.masksToBounds = truecenter.addSubview(indicator)self.addSubview(v)v.snp.makeConstraints { make inmake.edges.equalToSuperview()}center.snp.makeConstraints { make inmake.center.equalToSuperview()make.width.equalToSuperview()}indicator.snp.makeConstraints { make inmake.top.equalToSuperview()make.width.height.equalTo(32)make.centerX.equalToSuperview()}title.snp.makeConstraints { make inmake.top.equalTo(indicator.snp.bottom)make.bottom.equalToSuperview()make.centerX.equalToSuperview()}}required init?(coder: NSCoder) {fatalError("init(coder:) has not been implemented")}deinit {observation = nil}override func endRefreshing() {super.endRefreshing()self.isLocalRefreshing = falseindicator.progress = 0}
}extension UITableView {func endRefreshing() {if ((self.refreshControl?.isKind(of: XRefreshControl.self)) != nil) {self.refreshControl?.endRefreshing()}}
}
加强版本代码
class XRefreshControl: UIRefreshControl {var observation: NSKeyValueObservation?var isLocalRefreshing: Bool = falselet indicator = UIProgressView(progressViewStyle: .bar)var refreshingBlock: (()->Void)?var displayLink: CADisplayLink?var targetDuration: CGFloat = 3var fireDate: Date = .nowvar endRefreshingDate: Date = .nowvar title = UILabel()var colors: [UIColor] = [UIColor(hex: "ffbe0b"),UIColor(hex: "fb5607"),UIColor(hex: "ff006e"),UIColor(hex: "8338ec"),UIColor(hex: "3a86ff"),]var speedViews: [UIView] = []var blockViews: [UIView] = []// 背景var contentView = UIView()override init(frame: CGRect) {super.init(frame: frame)observation = observe(\.frame,options: .new) { [weak self] object, change inif self?.isRefreshing == true {if self?.isLocalRefreshing == false {if self?.refreshingBlock != nil {self?.refreshingBlock!()}self?.startAnimation()}self?.isLocalRefreshing = true} else {let height = change.newValue!.heightself?.dragEffect(distance: height)}}}convenience init(refreshingBlock: @escaping ()->Void) {self.init(frame: .zero)self.refreshingBlock = refreshingBlockself.layer.masksToBounds = trueself.backgroundColor = .whitecontentView.backgroundColor = .redself.addSubview(contentView)let center = UIView()contentView.addSubview(center)title.text = "下拉加载"title.textColor = .blackcenter.addSubview(title)center.addSubview(indicator)for _ in 0...6 {let v = UIView()v.backgroundColor = .whitespeedViews.append(v)contentView.addSubview(v)}for _ in 0..<10 {let v = UIView()v.backgroundColor = .whiteblockViews.append(v)contentView.addSubview(v)}contentView.snp.makeConstraints { make inmake.edges.equalToSuperview()}center.snp.makeConstraints { make inmake.center.equalToSuperview()}indicator.snp.makeConstraints { make inmake.left.top.right.equalToSuperview()make.width.equalTo(120)make.height.equalTo(6)}title.snp.makeConstraints { make inmake.top.equalTo(indicator.snp.bottom).offset(10)make.bottom.equalToSuperview()make.centerX.equalToSuperview()}}required init?(coder: NSCoder) {fatalError("init(coder:) has not been implemented")}deinit {observation = nilself.displayLink?.remove(from: RunLoop.current, forMode: .common)}func dragEffect(distance: CGFloat) {let diff = abs(endRefreshingDate.timeIntervalSinceNow)if diff < 0.5 {return}let precent = min(abs(distance/140),1)let value = precent * 8 * CGFloat.piself.indicator.progress = 1let opacity = Float(sin(value))
// print("opacity \(opacity)")self.indicator.layer.opacity = opacityself.title.text = "下拉加载"for i in 0..<3 {let xx = (self.frame.size.width / 12.0) * CGFloat(i+1)var yy = abs(distance/2)-2yy += sin(distance/10 + CGFloat(i+1)*10)*6speedViews[i].frame = .init(x: xx, y: yy, width: 2, height: 4)}for i in 3..<6 {var x = (self.frame.size.width / 12.0) * CGFloat(i+1-3)x += self.frame.width * 2.0 / 3.0var yy = abs(distance/2)-2yy += sin(distance/10 + CGFloat(i+1-3)*10)*6speedViews[i].frame = .init(x: x, y: yy, width: 2, height: 4)}for i in 0..<blockViews.count {blockViews[i].frame = .init(x: 0, y: 0, width: 0, height: 0)}}func startAnimation() {displayLink = CADisplayLink(target: self, selector: #selector(update))displayLink?.add(to: RunLoop.current, forMode: .common)fireDate = .nowself.indicator.layer.opacity = 1self.indicator.progress = 1self.title.text = "加载中"let width = self.frame.widthfor i in 0..<blockViews.count {let size = CGFloat.random(in: 4...8)let x = CGFloat.random(in: 0...width)blockViews[i].frame = .init(x: x, y: 0, width: size, height: size)}}@objc func update(_ displayLink: CADisplayLink) {let diff = abs(fireDate.timeIntervalSinceNow)var precent = diff / targetDurationprecent = min(precent, 1)self.indicator.progress = Float(precent)contentView.backgroundColor = colors[Int(diff*3)%colors.count]for i in 0..<3 {var xx = (self.frame.size.width / 12.0) * CGFloat(i+1)var yy = self.frame.height/2-12if i == 1 {yy += sin(CGFloat(diff)*6) * 2xx += sin(CGFloat(diff)*6)} else {yy += sin(CGFloat(diff)*6) * 4xx += sin(CGFloat(diff)*6 + CGFloat(i+1))}speedViews[i].frame = .init(x: xx, y: yy, width: 2, height: 24)}for i in 3..<6 {var xx = (self.frame.size.width / 12.0) * CGFloat(i+1-3)xx += self.frame.width * 2.0 / 3.0var yy = self.frame.height/2-12if i == 4 {yy += sin(CGFloat(diff)*6) * 2xx += sin(CGFloat(diff)*6)} else {yy += sin(CGFloat(diff)*6) * 4xx += sin(CGFloat(diff)*6 + CGFloat(i+1-3))}speedViews[i].frame = .init(x: xx, y: yy, width: 2, height: 24)}for i in 0..<self.blockViews.count {var x = self.blockViews[i].frame.origin.xvar y = self.blockViews[i].frame.origin.y + self.blockViews[i].frame.width / 4if y > self.contentView.frame.height {y = 0x = CGFloat.random(in: 0...self.contentView.frame.width)}self.blockViews[i].frame = .init(origin: .init(x: x, y: y), size: self.blockViews[i].frame.size)}}override func endRefreshing() {super.endRefreshing()self.isLocalRefreshing = falseself.displayLink?.remove(from: RunLoop.current, forMode: .common)endRefreshingDate = .nowself.title.text = "加载完毕"}
}
extension UITableView {func endRefreshing() {if ((self.refreshControl?.isKind(of: XRefreshControl.self)) != nil) {self.refreshControl?.endRefreshing()}}
}
extension UIColor {/// 使用 #FFFFFF 来初始化颜色convenience init(hex: String, alpha: CGFloat = 1.0) {var hexFormatted: String = hex.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines).uppercased()if hexFormatted.hasPrefix("#") {hexFormatted = String(hexFormatted.dropFirst())}if hexFormatted.hasPrefix("0x") {hexFormatted = String(hexFormatted.dropFirst())hexFormatted = String(hexFormatted.dropFirst())}assert(hexFormatted.count == 6, "Invalid hex code used.")var rgbValue: UInt64 = 0Scanner(string: hexFormatted).scanHexInt64(&rgbValue)self.init(red: CGFloat((rgbValue & 0xFF0000) >> 16) / 255.0,green: CGFloat((rgbValue & 0x00FF00) >> 8) / 255.0,blue: CGFloat(rgbValue & 0x0000FF) / 255.0,alpha: alpha)}
}
参考地址
相关文章:
iOS自定义下拉刷新控件
自定义下拉刷新控件 概述 用了很多的别人的下拉刷新控件,想写一个玩玩,自定义一个在使用的时候也会比较有意思。使应用更加的灵动一些,毕竟谁不喜欢各种动画恰到好处的应用呢。 使用方式如下: tableview.refreshControl XRef…...
Springboot写单元测试
导入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><exclusions><exclusion><groupId>org.junit.vintage</groupId><artifactId>junit-vintag…...
一篇文章教你使用Docker本地化部署Chatgpt(非api,速度非常快!!!)及裸连GPT的方式(告别镜像GPT)
本地搭建ChatGPT(非api调用) 第一种方法:使用Docker本地化部署第一步,下载安装Docker登录GPT 第二种方法:不部署项目,直接连接 第一种方法:使用Docker本地化部署 这种方法的好处就是没有登录限…...
前馈神经网络dropout实例
直接看代码。 (一)手动实现 import torch import torch.nn as nn import numpy as np import torchvision import torchvision.transforms as transforms import matplotlib.pyplot as plt#下载MNIST手写数据集 mnist_train torchvision.datasets.MN…...
Android DataStore:安全存储和轻松管理数据
关于作者:CSDN内容合伙人、技术专家, 从零开始做日活千万级APP。 专注于分享各领域原创系列文章 ,擅长java后端、移动开发、人工智能等,希望大家多多支持。 目录 一、导读二、概览三、使用3.1 Preferences DataStore添加依赖数据读…...
opencv进阶12-EigenFaces 人脸识别
EigenFaces 通常也被称为 特征脸,它使用主成分分析(Principal Component Analysis,PCA) 方法将高维的人脸数据处理为低维数据后(降维),再进行数据分析和处理,获取识别结果。 基本原理…...
The internal rate of return (IRR)
内部收益率 NPV(Net Present Value)_spencer_tseng的博客-CSDN博客...
半导体自动化专用静电消除器主要由哪些部分组成
半导体自动化专用静电消除器是一种用于消除半导体生产过程中的静电问题的设备。由于半导体制造过程中对静电的敏感性,静电可能会对半导体器件的质量和可靠性产生很大的影响,甚至造成元件损坏。因此,半导体生产中采用专用的静电消除器是非常重…...
【C++入门到精通】C++入门 —— deque(STL)
阅读导航 前言一、deque简介1. 概念2. 特点 二、deque使用1. 基本操作(增、删、查、改)2. 底层结构 三、deque的缺陷四、 为什么选择deque作为stack和queue的底层默认容器总结温馨提示 前言 文章绑定了VS平台下std::deque的源码,大家可以下载…...
Codeforces Round 893 (Div. 2) D.Trees and Segments
原题链接:Problem - D - Codeforces 题面: 大概意思就是让你在翻转01串不超过k次的情况下,使得a*(0的最大连续长度)(1的最大连续长度)最大(1<a<n)。输出n个数&…...
SpringBoot + Vue 前后端分离项目 微人事(九)
职位管理后端接口设计 在controller包里面新建system包,再在system包里面新建basic包,再在basic包里面创建PositionController类,在定义PositionController类的接口的时候,一定要与数据库的menu中的url地址到一致,不然…...
【业务功能篇71】Cglib的BeanCopier进行Bean对象拷贝
选择Cglib的BeanCopier进行Bean拷贝的理由是, 其性能要比Spring的BeanUtils,Apache的BeanUtils和PropertyUtils要好很多, 尤其是数据量比较大的情况下。 BeanCopier的主要作用是将数据库层面的Entity转化成service层的POJO。BeanCopier其实已…...
让eslint的错误信息显示在项目界面上
1.需求描述 效果如下 让eslint中的错误,显示在项目界面上 2.问题解决 1.安装 vite-plugin-eslint 插件 npm install vite-plugin-eslint --save-dev2.配置插件 // vite.config.js import { defineConfig } from vite import vue from vitejs/plugin-vue import e…...
手摸手带你实现一个开箱即用的Node邮件推送服务
目录 编辑 前言 准备工作 邮箱配置 代码实现 服务部署 使用效果 题外话 写在最后 相关代码: 前言 由于邮箱账号和手机号的唯一性,通常实现验证码的校验时比较常用的两种方式是手机短信推送和邮箱推送,此外,邮件推送服…...
【Linux网络】网络编程套接字 -- 基于socket实现一个简单UDP网络程序
认识端口号网络字节序处理字节序函数 htonl、htons、ntohl、ntohs socketsocket编程接口sockaddr结构结尾实现UDP程序的socket接口使用解析socket处理 IP 地址的函数初始化sockaddr_inbindrecvfromsendto 实现一个简单的UDP网络程序封装服务器相关代码封装客户端相关代码实验结…...
Python学习笔记第六十四天(Matplotlib 网格线)
Python学习笔记第六十四天 Matplotlib 网格线普通网格线样式网格线 后记 Matplotlib 网格线 我们可以使用 pyplot 中的 grid() 方法来设置图表中的网格线。 grid() 方法语法格式如下: matplotlib.pyplot.grid(bNone, whichmajor, axisboth, )参数说明:…...
机器学习与模式识别3(线性回归与逻辑回归)
一、线性回归与逻辑回归简介 线性回归主要功能是拟合数据,常用平方误差函数。 逻辑回归主要功能是区分数据,找到决策边界,常用交叉熵。 二、线性回归与逻辑回归的实现 1.线性回归 利用回归方程对一个或多个特征值和目标值之间的关系进行建模…...
vue启动配置npm run serve,动态环境变量,根据不同环境访问不同域名
首先创建不同环境的配置文件,比如域名和一些常量,创建一个env文件,先看看文件目录 env.dev就是dev环境的域名,.test就是test环境域名,其他同理,然后配置package.json文件 {"name": "require-admin&qu…...
HTML <strike> 标签
HTML5 中不支持 <strike> 标签在 HTML 4 中用于定义删除线文本。 定义和用法 <strike> 标签可定义加删除线文本定义。 浏览器支持 元素ChromeIEFirefoxSafariOpera<strike>YesYesYesYesYes 所有浏览器都支持 <strike> 标签。 HTML 与 XHTML 之间…...
数学建模-模型详解(1)
规划模型 线性规划模型: 当涉及到线性规划模型实例时,以下是一个简单的示例: 假设我们有两个变量 x 和 y,并且我们希望最大化目标函数 Z 5x 3y,同时满足以下约束条件: x > 0y > 02x y < 10…...
应用升级/灾备测试时使用guarantee 闪回点迅速回退
1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间, 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点,不需要开启数据库闪回。…...
k8s从入门到放弃之Ingress七层负载
k8s从入门到放弃之Ingress七层负载 在Kubernetes(简称K8s)中,Ingress是一个API对象,它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress,你可…...
2024年赣州旅游投资集团社会招聘笔试真
2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...
linux 下常用变更-8
1、删除普通用户 查询用户初始UID和GIDls -l /home/ ###家目录中查看UID cat /etc/group ###此文件查看GID删除用户1.编辑文件 /etc/passwd 找到对应的行,YW343:x:0:0::/home/YW343:/bin/bash 2.将标红的位置修改为用户对应初始UID和GID: YW3…...
DBAPI如何优雅的获取单条数据
API如何优雅的获取单条数据 案例一 对于查询类API,查询的是单条数据,比如根据主键ID查询用户信息,sql如下: select id, name, age from user where id #{id}API默认返回的数据格式是多条的,如下: {&qu…...
JS设计模式(4):观察者模式
JS设计模式(4):观察者模式 一、引入 在开发中,我们经常会遇到这样的场景:一个对象的状态变化需要自动通知其他对象,比如: 电商平台中,商品库存变化时需要通知所有订阅该商品的用户;新闻网站中࿰…...
Mysql中select查询语句的执行过程
目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析(Parser) 2.4、执行sql 1. 预处理(Preprocessor) 2. 查询优化器(Optimizer) 3. 执行器…...
Razor编程中@Html的方法使用大全
文章目录 1. 基础HTML辅助方法1.1 Html.ActionLink()1.2 Html.RouteLink()1.3 Html.Display() / Html.DisplayFor()1.4 Html.Editor() / Html.EditorFor()1.5 Html.Label() / Html.LabelFor()1.6 Html.TextBox() / Html.TextBoxFor() 2. 表单相关辅助方法2.1 Html.BeginForm() …...
es6+和css3新增的特性有哪些
一:ECMAScript 新特性(ES6) ES6 (2015) - 革命性更新 1,记住的方法,从一个方法里面用到了哪些技术 1,let /const块级作用域声明2,**默认参数**:函数参数可以设置默认值。3&#x…...
Pydantic + Function Calling的结合
1、Pydantic Pydantic 是一个 Python 库,用于数据验证和设置管理,通过 Python 类型注解强制执行数据类型。它广泛用于 API 开发(如 FastAPI)、配置管理和数据解析,核心功能包括: 数据验证:通过…...
