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…...
OpenMMLab MMTracking 目标跟踪算法库
MMTracking是OpenMMLab(商汤科技与港中文MMLab联合推出)体系下的一款开源视频目标感知工具箱。你可以把它理解为“视频版”的MMDetection,它将该领域内纷繁复杂的算法、数据集和评估标准,统一整合到了一个高效、模块化的框架中。 …...
函数调用(Function Calling)深度集成:让 AI 安全执行企业 API
系列导读 你现在看到的是《Spring AI 企业级集成与场景实践:从零搭建智能应用》的第 5/10 篇,当前这篇会重点解决:展示如何让 AI 安全可控地操作企业后端服务,实现真正的智能体能力。 上一篇回顾:第 4 篇《检索增强生成(RAG)实战:Spring AI 集成向量数据库实现知识问…...
3步解决Windows 10/11下PL-2303串口设备驱动失效问题
3步解决Windows 10/11下PL-2303串口设备驱动失效问题 【免费下载链接】pl2303-win10 Windows 10 driver for end-of-life PL-2303 chipsets. 项目地址: https://gitcode.com/gh_mirrors/pl/pl2303-win10 你是否遇到过这样的情况:在Windows 10或Windows 11系统…...
从App Inventor到数据解析:打造一个专属的Android蓝牙温湿度监测App(适配HC-05+Arduino)
从零构建Android蓝牙温湿度监测系统:App Inventor与Arduino实战指南 在物联网技术快速普及的今天,将传感器数据可视化呈现已成为许多创客和教育场景中的常见需求。本文将以DHT-11温湿度传感器为核心,通过HC-05蓝牙模块搭建Arduino与Android设…...
FABRK全栈框架:模块化设计与AI辅助开发实战解析
1. 项目概述:一个为AI时代而生的全栈开发框架如果你和我一样,在过去几年里反复搭建过各种SaaS应用、管理后台或者数据看板,你一定会对那种重复劳动感到厌倦。每次新项目启动,都要重新配置身份验证、集成支付、设计仪表盘组件、处理…...
AI开发者实战指南:从工具全景到本地知识库搭建
1. 从Awesome List到实战地图:一份AI开发者工具全景解析如果你是一名AI开发者、研究者,或者只是对构建AI应用充满好奇的技术爱好者,面对浩如烟海的工具、框架和平台,最头疼的恐怕就是“我该从哪里开始?”这个问题。网上…...
利用 JiuwenClaw AgentTeam 打造自动化研发团队
利用 JiuwenClaw AgentTeam 打造自动化研发团队 本文介绍如何通过 JiuwenClaw AgentTeam 构建自动化研发团队,实现字幕软件开发、AtomGit Issue/PR 智能处理与飞书文档同步。 目录 JiuwenClaw 平台概述 系统架构预置智能体类型 什么是 AgentTeams飞书群中添加机器人…...
如何在Windows上快速安装iPhone网络共享驱动:3分钟终极解决方案
如何在Windows上快速安装iPhone网络共享驱动:3分钟终极解决方案 【免费下载链接】Apple-Mobile-Drivers-Installer Powershell script to easily install Apple USB and Mobile Device Ethernet (USB Tethering) drivers on Windows! 项目地址: https://gitcode.c…...
别再复制粘贴了!手把手教你为51单片机LCD12864制作自定义中文字库(Keil C51环境)
从零构建51单片机LCD12864自定义中文字库的完整实战指南 在嵌入式显示领域,标准字库往往无法满足个性化需求。当我们需要在LCD12864屏幕上显示特殊符号、品牌LOGO或艺术字体时,自定义字库技术就成为关键突破点。本文将彻底解析从字模提取到ROM优化的全流…...
Claude智能优化器:提升AI应用开发效率的提示词工程中间件
1. 项目概述与核心价值 最近在折腾AI应用开发,特别是围绕Claude API做各种自动化工具时,发现一个挺普遍的问题:直接调用Claude API返回的答案,有时候会显得有点“啰嗦”或者“不够聚焦”。比如你让它写一段代码,它可能…...
