Swift实现的一个tableView可用的下拉刷新控件(自定义UIControl)

以下代码中的`约束布局框架`使用的是SnapKit

自定下拉刷新控件

SHRefreshControl.swift

//
//  SHRefreshControl.swift
//  weibo
//
//  Created by 邵瑞波 on 16/1/17.
//  Copyright © 2016年 邵瑞波. All rights reserved.
//
import UIKit
// 控件当前状态
enum SHRefreshControlState: Int {
    case Normal     = 0   // 正常状态
    case Pulling    = 1   // 松手可以刷新的状态
    case Refreshing = 2   // 正在刷新的状态
}
class SHRefreshControl: UIControl {
    
    var top: CGFloat = 0.0
    
    var currRefreshState: SHRefreshControlState {
        return refreshState
    }
    // 之前刷新状态
    private var oldRefreshState: SHRefreshControlState = .Normal
    // 当前刷新状态
    private var refreshState: SHRefreshControlState = .Normal {
        didSet {
            switch refreshState {
            case .Normal:  // 正常状态
                arrowIcon.hidden = false
                indicatorView.stopAnimating()
                labelTip.text = "下拉刷新"
                
                UIView.animateWithDuration(0.25, animations: { () -> Void in
                    self.arrowIcon.transform = CGAffineTransformIdentity
                })
                
                if oldRefreshState == .Refreshing {
                    print("xxxxxxxxx")
                    UIView.animateWithDuration(0.25, animations: { () -> Void in
                        self.scrollView?.contentInset.top -= self.frame.size.height
                    })
                }
                
                oldRefreshState = refreshState
            case .Pulling:  // 释放可以刷新的状态
                arrowIcon.hidden = false
                labelTip.text = "释放加载"
                
                UIView.animateWithDuration(0.25, animations: { () -> Void in
                    self.arrowIcon.transform = CGAffineTransformMakeRotation(CGFloat(M_PI))
                })
                
                oldRefreshState = refreshState
            case .Refreshing:  // 正在刷新的状态
                arrowIcon.hidden = true
                arrowIcon.transform = CGAffineTransformIdentity
                indicatorView.startAnimating()
                labelTip.text = "正在加载"
                // 这里要提前一步设置,否则后续发送事件时,是同步调用,这是的原来状态还没有改变,在.Normal中执行会出现问题
                oldRefreshState = refreshState
                
//                UIView.animateWithDuration(0.25, animations: { () -> Void in
//                    self.scrollView?.contentInset.top += self.frame.size.height
//                })
                
                UIView.animateWithDuration(0.25, animations: { () -> Void in
                    self.scrollView?.contentInset.top += self.frame.size.height
                    }, completion: { (_) -> Void in
                    self.sendActionsForControlEvents(UIControlEvents.ValueChanged)
                })
                
                // 如果发送事件的代码放无法保证与动画线程同步,则无法保证在控制器中处理事件时,动画已经执行完毕,这样会出现
                // 下拉刷新按钮没执行一次,则向下移动一段距离无法回去。
                // 这里要提前一步设置,否则后续发送事件时,是同步调用,这是的原来状态还没有改变,在.Normal中执行会出现问题
//                sendActionsForControlEvents(UIControlEvents.ValueChanged)
            }
            
            // 这里设置原来的状态放到每个case分支中单独设置,因为某些逻辑需要,都放到这里设置无法实现!!!!
//            oldRefreshState = refreshState
        }
    }
    
    private var scrollView: UIScrollView?
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupUI()
    }
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    private func setupUI() {
        backgroundColor = UIColor(white: 0.99, alpha: 1)
        
        let height: CGFloat = 40
        
        frame.origin.x = 0
        frame.origin.y = -height
        frame.size.height = height
        frame.size.width = kScreenW // 这个在被添加到父视图时会重新调整
        
        addSubview(arrowIcon)
        addSubview(indicatorView)
        addSubview(labelTip)
        
        let marginCenterX = 10
        
        labelTip.snp_makeConstraints { (make) -> Void in
            make.centerY.equalTo(self)
            make.centerX.equalTo(self).offset(marginCenterX)
        }
        arrowIcon.snp_makeConstraints { (make) -> Void in
            make.centerY.equalTo(self)
            make.trailing.equalTo(labelTip.snp_leading)
        }
        indicatorView.snp_makeConstraints { (make) -> Void in
            make.center.equalTo(arrowIcon)
        }
    }
    
    // 箭头
    private var arrowIcon: UIImageView = UIImageView(image: UIImage(named: "tableview_pull_refresh"))
    // 菊花
    private var indicatorView: UIActivityIndicatorView = {
        let indicatorView = UIActivityIndicatorView()
        indicatorView.hidesWhenStopped = true
        indicatorView.activityIndicatorViewStyle = .Gray
        return indicatorView
    }()
    // 文字
    private var labelTip: UILabel = {
        let label = UILabel(textColor: UIColor.lightGrayColor(), fontSize: 14)
        label.text = "下拉刷新"
        return label
    }()
    
    override func willMoveToSuperview(newSuperview: UIView?) {
        super.willMoveToSuperview(newSuperview)
        
        //scrollView?.removeObserver(self, forKeyPath: "contentOffset")
        
        if let scrollView = newSuperview as? UIScrollView {
            self.scrollView = scrollView
            
            // 添加对父视图`contentOffset`的监听
            self.scrollView?.addObserver(self, forKeyPath: "contentOffset", options: NSKeyValueObservingOptions.New, context: nil)
            
            // 根据父视图,调整自身宽度
            frame.size.width = scrollView.frame.size.width
        }
    }
    
    func endRefreshing() {
        refreshState = .Normal
    }
    
    // 如果使用 kvo 监听了 属性,则 该方法必须实现,否则会崩溃
    override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
        
        if refreshState == .Refreshing {
            return
        }
        
        if !(object as? NSObject)!.isKindOfClass(UIScrollView.self) {
            return
        }
        
        let scrollView = object as! UIScrollView
        
        let offSet = scrollView.contentOffset
        let top = scrollView.contentInset.top
        let height = self.frame.size.height
        
//        print("offSet=[\(offSet)]---[\(-(top + height))]")
        
        if scrollView.dragging {
            if offSet.y < -(top + height) {
                // 箭头翻转,如果在
                self.refreshState = .Pulling
                // 此时如果释放的话,下次执行会进入到 LOOP_REFRESHE 这里
            } else {
                self.refreshState = .Normal
            }
        } else {
            // LOOP_REFRESHE
            if oldRefreshState == .Refreshing {
                return
            }
            
            // 判断如果当前scrollView的偏移量小于,如下值,则翻转箭头,发送事件,开始刷新
            if oldRefreshState == .Pulling {
                refreshState = .Refreshing
            } else {
                refreshState = .Normal
            }
        }
        
    }
    
    deinit {
        scrollView?.removeObserver(self, forKeyPath: "contentOffset")
    }
}

在控制器中的代码如下:

// 创建控件
let shRefreshControl: SHRefreshControl = SHRefreshControl()
// 设置监听
shRefreshControl.addTarget(self, action: "refreshPro:", forControlEvents: UIControlEvents.ValueChanged)
// 添加到父类tableView
tableView.addSubview(shRefreshControl)


转载于:https://my.oschina.net/quntion/blog/604331

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值