Refresh for Swift

//
//  RefreshControl.swift
//  
//
//  Created by Long on 16/09/14.
//  Copyright © 2016年 Apple. All rights reserved.
//

import UIKit

//  自定义下拉刷新的状态
private enum RefreshType: Int {
    //  下拉刷新状态
    case normal = 0
    //  松手就刷新
    case pulling = 1
    //  正在刷新
    case refreshing = 2
}


//  自定义控件的高度
private let RefreshControlHeight: CGFloat = 50

//  自定义下拉刷新控件
class RefreshControl: UIControl {

    //  记录当前刷新的控件
    private var currentScrollView: UIScrollView?
    //  记录当前刷新状态
    private var myRefreshType: RefreshType = .normal {
        didSet {
            switch myRefreshType {
            case .normal:
                print("下拉刷新")
                //  箭头重置,箭头显示,关闭风火轮,内容改成下拉刷新
                pullDownImageView.isHidden = false
                UIView.animate(withDuration: 0.25,animations: {
                    //  重置箭头位置
                    self.pullDownImageView.transform = CGAffineTransform.identity
                })
                indicatorView.stopAnimating()
                messageLabel.text = "下拉刷新"
                
                //  判断上一次刷新状态是正在属性,让重置默认位置
                //  oldValue 表示上一次存储的值
                if oldValue == .refreshing {
                    UIView.animate(withDuration: 0.25,animations: {
                        //  设置停留位置,核心代码,重置停留位置
                        self.currentScrollView?.contentInset.top -= RefreshControlHeight
                    })
                }
               
                
                
            case .pulling:
                print("松手就刷新")
                //  箭头调转,内容修改成松手就刷新
                UIView.animate(withDuration: 0.25,animations: {
                    //  调转箭头位置
                    self.pullDownImageView.transform = CGAffineTransform(rotationAngle: CGFloat(M_PI))
                })
                messageLabel.text = "松手就刷新"
                
                
            case .refreshing:
                print("正在刷新")
                //  箭头隐藏,开启风火轮,内容改成正在刷新
                pullDownImageView.isHidden = true
                //  开启风火轮
                indicatorView.startAnimating()
                messageLabel.text = "正在刷新..."
                
                UIView.animate(withDuration: 0.25,animations: { 
                    //  设置停留位置,核心代码
                    self.currentScrollView?.contentInset.top += RefreshControlHeight
                })
                
                //  通知外界刷新数据 (核心代码)
                sendActions(for: .valueChanged)
                
                
                
                
                
            }
        }
    }
    
    // MARK: - 懒加载控件
    //  下拉箭头图片
    fileprivate lazy var pullDownImageView: UIImageView = UIImageView(image: UIImage(named: "tableview_pull_refresh"))
    //  下拉刷新内容 label
    fileprivate lazy var messageLabel: UILabel = {
        let label = UILabel()
        label.text = "下拉刷新"
        label.font = UIFont.systemFont(ofSize: 11)
        label.textColor = UIColor.gray
        return label
    
    }()
    //  风火轮
    fileprivate lazy var indicatorView: UIActivityIndicatorView = UIActivityIndicatorView(activityIndicatorStyle: .gray)
    
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        //  获取屏幕的宽度
        let screenWidth = UIScreen.main.bounds.size.width
        //  设置 frame
        self.frame = CGRect(x: 0,y: -RefreshControlHeight,width: screenWidth,height: RefreshControlHeight)
        setupUI()
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    //  添加控件设置约束
    private func setupUI() {
        backgroundColor = UIColor.red
        //  添加子控件设置约束
        addSubview(pullDownImageView)
        addSubview(messageLabel)
        addSubview(indicatorView)
        
        
        //  使用系统布局 autolayout 需要关闭 Autoresizing
        pullDownImageView.translatesAutoresizingMaskIntoConstraints = false
        messageLabel.translatesAutoresizingMaskIntoConstraints = false
        indicatorView.translatesAutoresizingMaskIntoConstraints = false
        
        //  添加约束
        addConstraint(NSLayoutConstraint(item: pullDownImageView,attribute: .centerX,relatedBy: .equal,toItem: self,multiplier: 1,constant: -35))
        addConstraint(NSLayoutConstraint(item: pullDownImageView,attribute: .centerY,constant: 0))
        
        addConstraint(NSLayoutConstraint(item: messageLabel,attribute: .leading,toItem: pullDownImageView,attribute: .trailing,constant: 0))

        addConstraint(NSLayoutConstraint(item: indicatorView,constant: 0))
        
        addConstraint(NSLayoutConstraint(item: indicatorView,constant: 0))
        
       
        
        
        
    }
    
    //  获取父控件,当前控件将要添加到那个父视图上面
    override func willMove(toSuperview newSuperview: UIView?) {
        print(newSuperview)
        //  判断是否是可以滚动的控件
        if let scrollView = newSuperview as? UIScrollView {
            //  表示是 UIScrollView 的子类,可以监听器滚动
            //  可以使用 kvo 监听 contentOffSet 值得改变
            
            //  [.new,.old] 表示可以监听新值和旧值得改变
            scrollView.addObserver(self,forKeyPath: "contentOffset",options: .new,context: nil)
            //  记录刷新视图控件
            currentScrollView = scrollView
            
        }
        
        
        
        
    }
    
    //  kvo监听方法
    override func observeValue(forKeyPath keyPath: String?,of object: Any?,change: [NSKeyValueChangeKey : Any]?,context: UnsafeMutableRawPointer?) {
        
        guard let scrollView = currentScrollView else {
            return
        }
        
        //  代码执行到此,表示 scrollView 不为 nil
        
        //  计算临界点
        let maxY = -(scrollView.contentInset.top + RefreshControlHeight)
        //  获取 y 轴偏移量
        let contentOffSetY = scrollView.contentOffset.y
        
       
        //  判断是否拖动
        //  拖动情况下只有两种状态 .normal,pulling
        if scrollView.isDragging {
            //  表示在拖动 判断的核心
            if contentOffSetY < maxY && myRefreshType == .normal  {
                myRefreshType = .pulling
                print("pulling")
            } else if contentOffSetY >= maxY && myRefreshType == .pulling {
                myRefreshType = .normal
                print("normal")
            }
            
            
            
            
        } else {
            //  表示松手,只有 pulling 状态 才能进入正在刷新
            
            if myRefreshType == .pulling {
                myRefreshType = .refreshing
                print("正在刷新")
            }
            
            
            
        }
        
        
        
    }
    
    //  结束刷新
    func endRefreshing() {
        myRefreshType = .normal
    }
    
    deinit {
        //  移除 kvo
        currentScrollView?.removeObserver(self,forKeyPath: "contentOffset")
    }
    

}

使用

    //  自定义下拉刷新控件
    fileprivate lazy var refreshControl: RefreshControl = RefreshControl()
   tableView.addSubview(refreshControl)
 refreshControl.addTarget(self,action: #selector(更新数据的方法名),for: UIControlEvents.valueChanged)


相关文章

Swift 正式开源!Swift 团队很高兴宣布 Swift 开始开源新篇章。自从苹果发布 Swfit 编程语言,就成为了...
快,快,快!动动您的小手,分享给更多朋友! 苹果去年推出了全新的编程语言Swift,试图让iOS开发更简单...
开发者(KaiFaX) 面向开发者、程序员的专业平台! 和今年年初承诺的一样,苹果贴出了Swift语言的源码,...
本文由@Chun发表于Chun Tips :http://chun.tips/blog/2014/12/11/shi-yong-swift-gou-jian-zi-ding-yi...
本文由CocoaChina译者leon(社区ID)翻译 原文:THE RIGHT WAY TO WRITE A SINGLETON 在之前的帖子里聊过...
本文由CocoaChina译者leon(社区ID)翻译 原文:THE RIGHT WAY TO WRITE A SINGLETON 在之前的帖子里聊过...