iOS自定义下拉刷新控件

这篇具有很好参考价值的文章主要介绍了iOS自定义下拉刷新控件。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

自定义下拉刷新控件

概述

用了很多的别人的下拉刷新控件,想写一个玩玩,自定义一个在使用的时候也会比较有意思。使应用更加的灵动一些,毕竟谁不喜欢各种动画恰到好处的应用呢。

使用方式如下:

tableview.refreshControl = XRefreshControl.init(refreshingBlock: {
    DispatchQueue.main.asyncAfter(deadline: .now() + 5) { [weak self] in
        self?.table.endRefreshing()
    }
})

下边展示一下效果。

iOS自定义下拉刷新控件,iOS开发,ios,swift

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

iOS自定义下拉刷新控件,iOS开发,ios,swift

继承 UIRefreshControl,然后再其上直接添加view就能实现需要的加载效果,尝试发现自定义的类需要把背景色设置一下,要不然会有一下拉整体都显示出来的问题,而且最好在view上再加一个view整体给铺上,在设置一个背景色,把小菊花给盖上。

简单版本代码

import Foundation
import UIKit
import SnapKit

class XRefreshControl: UIRefreshControl {
    var observation: NSKeyValueObservation?
    var isLocalRefreshing: Bool = false
    let indicator = UIProgressView(progressViewStyle: .bar)
    var refreshingBlock: (()->Void)?
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        observation = observe(
            \.frame,
             options: .new
        ) { [weak self] object, change in
            if self?.isRefreshing == true {
                if self?.isLocalRefreshing == false {
                    if self?.refreshingBlock != nil {
                        self?.refreshingBlock!()
                    }
                }
                self?.isLocalRefreshing = true
            } else {
                let height = change.newValue!.height
                self?.indicator.progress = min(Float(abs(height / 60)), 1)
            }
        }
    }
    convenience init(refreshingBlock: @escaping ()->Void) {
        self.init(frame: .zero)
        
        self.refreshingBlock = refreshingBlock
        
        self.layer.masksToBounds = true
        self.backgroundColor = .red
        
        let v = UIView()
        v.backgroundColor = .red
        
        let center = UIView()
        v.addSubview(center)
        
        let title = UILabel()
        title.text = "加载中"
        title.textColor = .black
        center.addSubview(title)
        
        indicator.layer.masksToBounds = true
        center.addSubview(indicator)
        
        self.addSubview(v)
        v.snp.makeConstraints { make in
            make.edges.equalToSuperview()
        }
        center.snp.makeConstraints { make in
            make.center.equalToSuperview()
            make.width.equalToSuperview()
        }
        indicator.snp.makeConstraints { make in
            make.top.equalToSuperview()
            make.width.height.equalTo(32)
            make.centerX.equalToSuperview()
        }
        title.snp.makeConstraints { make in
            make.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 = false
        indicator.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 = false
    let indicator = UIProgressView(progressViewStyle: .bar)
    var refreshingBlock: (()->Void)?
    var displayLink: CADisplayLink?
    var targetDuration: CGFloat = 3
    var fireDate: Date = .now
    var endRefreshingDate: Date = .now
    var 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 in
            if self?.isRefreshing == true {
                if self?.isLocalRefreshing == false {
                    if self?.refreshingBlock != nil {
                        self?.refreshingBlock!()
                    }
                    self?.startAnimation()
                }
                self?.isLocalRefreshing = true
            } else {
                let height = change.newValue!.height
                self?.dragEffect(distance: height)
            }
        }
    }
    
    convenience init(refreshingBlock: @escaping ()->Void) {
        self.init(frame: .zero)
        
        self.refreshingBlock = refreshingBlock
        
        self.layer.masksToBounds = true
        self.backgroundColor = .white
        
        contentView.backgroundColor = .red
        self.addSubview(contentView)
        
        let center = UIView()
        contentView.addSubview(center)
        
        title.text = "下拉加载"
        title.textColor = .black
        center.addSubview(title)
        
        center.addSubview(indicator)
        
        for _ in 0...6 {
            let v = UIView()
            v.backgroundColor = .white
            speedViews.append(v)
            contentView.addSubview(v)
        }
        
        for _ in 0..<10 {
            let v = UIView()
            v.backgroundColor = .white
            blockViews.append(v)
            contentView.addSubview(v)
        }
        
        contentView.snp.makeConstraints { make in
            make.edges.equalToSuperview()
        }
        center.snp.makeConstraints { make in
            make.center.equalToSuperview()
        }
        indicator.snp.makeConstraints { make in
            make.left.top.right.equalToSuperview()
            make.width.equalTo(120)
            make.height.equalTo(6)
        }
        title.snp.makeConstraints { make in
            make.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 = nil
        self.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.pi
        self.indicator.progress = 1
        let opacity = Float(sin(value))
//        print("opacity \(opacity)")
        self.indicator.layer.opacity = opacity
        self.title.text = "下拉加载"
        
        for i in 0..<3 {
            let xx = (self.frame.size.width / 12.0) * CGFloat(i+1)
            var yy = abs(distance/2)-2
            yy += sin(distance/10 + CGFloat(i+1)*10)*6
            speedViews[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.0
            var yy = abs(distance/2)-2
            yy += sin(distance/10 + CGFloat(i+1-3)*10)*6
            speedViews[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 = .now
        self.indicator.layer.opacity = 1
        self.indicator.progress = 1
        self.title.text = "加载中"
        
        let width = self.frame.width
        for 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 / targetDuration
        precent = 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-12
            if i == 1 {
                yy += sin(CGFloat(diff)*6) * 2
                xx += sin(CGFloat(diff)*6)
            } else {
                yy += sin(CGFloat(diff)*6) * 4
                xx += 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.0
            var yy = self.frame.height/2-12
            if i == 4 {
                yy += sin(CGFloat(diff)*6) * 2
                xx += sin(CGFloat(diff)*6)
            } else {
                yy += sin(CGFloat(diff)*6) * 4
                xx += 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.x
            var y = self.blockViews[i].frame.origin.y + self.blockViews[i].frame.width / 4
            if y > self.contentView.frame.height {
                y = 0
                x = 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 = false
        self.displayLink?.remove(from: RunLoop.current, forMode: .common)
        endRefreshingDate = .now
        self.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 = 0
        Scanner(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)
    }
}

参考地址文章来源地址https://www.toymoban.com/news/detail-662030.html

到了这里,关于iOS自定义下拉刷新控件的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • iOS开发Swift-闭包

    将很长的闭包表达式作为最后一个参数传递给函数,不用写出他的参数标签。 嵌套函数可捕获其外部函数所有参数、变量、常量。 当一个闭包作为一个参数传到一个函数中,但闭包在函数返回之后才被执行,则称闭包逃逸。 标注@escaping,表示允许闭包逃逸。  包装传递给函数

    2024年02月11日
    浏览(40)
  • iOS开发Swift-函数

     (1)无参函数 (2)多参函数 (3)无返回值 (4)多重返回值 (5)可选元组返回类型(元组可以是nil) (6)隐式返回的函数 任一可以被写成一行return的函数,return(x) + for。 调用的时候: 方法名(for: 参数) (1)指定参数标签 (2)忽略参数标签 (3)默认参数值 (4)可变参数 一个可变参数可接受0个或多

    2024年02月11日
    浏览(39)
  • iOS开发Swift-枚举

    枚举:一组相关的值定义了一个共同的类型,使你可以在代码中以类型安全的方式来使用这些值。 原始值:定义枚举时被预先填充的值。 (1)整数为原始值时,隐式赋值递增1。未设置原始值时,默认为0,之后递增1. (2)字符串为原始值,隐式赋值为枚举成员的名称。

    2024年02月11日
    浏览(36)
  • iOS开发Swift-基础部分

    系统可通过赋初始值进行自动推断。 平时可加可不加,同一行中有两句话必须加。 Int           UInt(最好不用) Double 64位 很大/高精度情况下使用 15位小数 Float 32位 对精度要求不高的情况下用 6位小数 十进制数   17 二进制 0b前缀 0b10001 八进制 0o前缀 0o21 十六进制 0x前缀

    2024年02月11日
    浏览(40)
  • iOS开发系列--Swift语言

    Swift是苹果2014年推出的全新的编程语言,它继承了C语言、ObjC的特性,且克服了C语言的兼容性问题。Swift发展过程中不仅保留了ObjC很多语法特性,它也借鉴了多种现代化语言的特点,在其中你可以看到C#、Java、Javascript、Python等多种语言的影子。同时在2015年的WWDC上苹果还宣布

    2024年02月06日
    浏览(39)
  • iOS开发Swift-类型转换

    1.Int或Double转字符串 2.Double转Int(去掉小数点后面的) 3.Int转Double 4.向上转型 5.向下转型

    2024年02月09日
    浏览(30)
  • iOS开发Swift-集合类型

    集合基本类型:数组 Array (有序), 集合 Set (无序不重复), 字典 Dictionary (无序键值对) (1)数组的表示 (2)创建空数组 (3)带值数组 (4)两数组相加创建数组 (5)字面量创造数组 (6)访问数组 (7)添加 (8)修改 (9)删除 (10)遍历 同时需要索引和值时: (1)集合的表示 (2)构造一个集合 (3)字面

    2024年02月11日
    浏览(38)
  • iOS开发Swift-控制流

    (1)复合匹配 (2)区间匹配 (3)元组匹配 (4)值绑定匹配 (5)where continue, break, fallthrough, return, throw continue: 停止本次循环,开始下次循环 break: 立即结束整个控制流。可以使用break忽略switch的分支。 fallthrough贯穿: switch中的case加入贯穿,case会穿透到下一个case/ default。

    2024年02月11日
    浏览(41)
  • iOS(一):Swift纯代码模式iOS开发入门教程

    1.修改 AppDelegate.swift 和 ViewController.swift 文件 2.删除 SceneDelegate.swift 和 Main.storyboard 文件 3.修改如图所示项 安装 CocoaPods 初始化项目(添加 Podfile 配置文件) 修改 Podfile 文件 安装 打开 ExDemoApp.xcworkspace 项目并向 ViewController.swift 添加示例代码 运行效果 安装 QMUIKit 方式一:触发

    2024年02月09日
    浏览(33)
  • iOS开发Swift-基本运算符

    一元 单一操作对象 -a    !b    c! 二元 两个操作对象 2 + 3 三元 三目运算符 a ? b : c 赋值运算符不返回任何值,所以 if x = y { ... } 无效。 +   -   *   / 默认不允许数值运算中溢出。 溢出运算符:   a + b a % b = 余数 a = (b * 倍数) + 余数 所以a % b = a % -b ==     !=         

    2024年02月11日
    浏览(35)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包