自定义下载刷新分析:
1. 系统的下拉刷新 UIRefreshControl , 没有继承系统的下拉刷新, 而继承UIControl
2. 自定义UIControl, 系统的下拉刷新默认有宽和高, 自定义的时候在init构造函数中设置固定的宽和高和y值
3. 添加子控件(菊花, 文字一些控件)设置约束
4. 下拉刷新线索: contentOffset. y值发生变化时, 对应的效果才会变化
5. 使用KVO监听 contentOffset 这个属性值的改变
6. 把监听方法移动到自定义的UIControl里
7. 分析下拉刷新的三种状态: 正常状态, 释放状态, 加载数据状态
8. 结合 contentOffset. y 和 UISrollView的dragging 这个属性来分析临界值
9. 记录这个状态值得改变, 分别实现对应的效果
10. 优化 - 用一个标记来排除状态的多次改变(这里用的是枚举)
11. 根据效果遇到一些小bug:
1. 加载中的时候视图下沉一下
2. 加载数据需要调用UIControl 的 sendActionsForControlEvents 方法
3. 实现一下加载完成后恢复视图的问题
下面直接上swift的代码(OC的话思路是一样的)
自定义CNRefreshView
//
// CNRefreshView.swift
// Sina
//
// Created by 晟楠 on 16/5/4.
// Copyright © 2016年 晟楠. All rights reserved.
//
import UIKit
// 记录状态的枚举
enum CNRefreshStatus: Int {
/// 正常状态
case Nomal = 1
/// 释放去加载数据状态
case pulling = 2
/// 加载数据状态
case loading = 3
}
class CNRefreshView: UIControl {
/// UIScrollView类型的属性
var scrollView: UIScrollView?
/// 记录状态的属性
var status: CNRefreshStatus = .Nomal {
didSet {
// 统一处理事件
switch status {
case .Nomal: messageLable.text = "下拉刷新"
UIView.animateWithDuration(0.25, animations: { () -> Void in
// 恢复箭头
self.arrowImageView.transform = CGAffineTransformIdentity
})
// 显示箭头
arrowImageView.hidden = false
// 停止菊花转动
loadingView.stopAnimating()
// 隐藏菊花
loadingView.hidden = true
case .pulling: messageLable.text = "释放刷新"
// 旋转箭头
UIView.animateWithDuration(0.25, animations: { () -> Void in
self.arrowImageView.transform = CGAffineTransformMakeRotation(CGFloat(M_PI))
})
case .loading: messageLable.text = "加载中"
UIView.animateWithDuration(0.25, animations: { () -> Void in
// 让加载的view停留一下
var inset = self.scrollView!.contentInset
inset.top = inset.top + 50
self.scrollView?.contentInset = inset
})
// 隐藏箭头
arrowImageView.hidden = true
// 菊花开启
loadingView.startAnimating()
// 菊花显示
loadingView.hidden = false
// 是加载数据状态要去加载数据
// 根据事件去触发方法
sendActionsForControlEvents(UIControlEvents.ValueChanged)
}
}
}
// MARK: - 1.重写父类构造函数
// 需要制定布局
override init(frame: CGRect) {
super.init(frame: frame)
// 添加子控件和设置子控件约束
setUpUI()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: 子控件添加到父控件上回调用此方法
override func willMoveToSuperview(newSuperview: UIView?) {
super.willMoveToSuperview(newSuperview)
printLog(newSuperview)
// 转换成UIScrollView, 集成UIScrollView的子类都可以用
if newSuperview is UIScrollView {
scrollView = newSuperview as? UIScrollView
// MARK: - KVO监听tableView的contentOffset
scrollView?.addObserver(self, forKeyPath: "contentOffset", options: NSKeyValueObservingOptions.New, context: nil)
}
}
// MARK: - 实现KVO要监听的方法
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
// 判断dragging进行更改标记
if scrollView!.dragging {
if scrollView?.contentOffset.y < -140 && status == .Nomal {
status = .pulling
} else if scrollView?.contentOffset.y >= -140 && status == .pulling {
status = .Nomal
}
} else {
if scrollView?.contentOffset.y < -140 && status == .pulling {
status = .loading
}
}
}
// MARK: - 数据加载完成后调用
func endRefreshing() {
if status == .loading {
// 让视图位置恢复
UIView.animateWithDuration(0.25, animations: { () -> Void in
// 停留一下view
var inset = self.scrollView!.contentInset
inset.top = inset.top - 50
self.scrollView?.contentInset = inset
})
}
// 修改标记回到正常状态
status = .Nomal
}
// MARK: 2.添加子控件和设置子控件约束
private func setUpUI() {
// 自定义子控件
backgroundColor = UIColor.clearColor()
// 设置宽高
var frame = self.frame
frame.size = CGSizeMake(kUIScreenWidth, 50)
frame.origin.y = -50
self.frame = frame
// 添加控件
addSubview(arrowImageView)
addSubview(messageLable)
addSubview(loadingView)
// 设置约束
loadingView.snp_makeConstraints { (make) -> Void in
make.centerX.equalTo(self.snp_centerX).offset(-30)
make.centerY.equalTo(self.snp_centerY)
}
arrowImageView.snp_makeConstraints { (make) -> Void in
make.centerX.equalTo(self.snp_centerX).offset(-30)
make.centerY.equalTo(self.snp_centerY)
}
messageLable.snp_makeConstraints { (make) -> Void in
make.centerY.equalTo(self.snp_centerY)
make.left.equalTo(arrowImageView.snp_right).offset(-5)
}
}
// MARK: 3. 懒加载控件
// 箭头
private lazy var arrowImageView: UIImageView = {
let img = UIImageView()
img.image = UIImage(named: "tableview_pull_refresh")
return img
}()
// 菊花
private lazy var indicatorImageView: UIImageView = {
let img = UIImageView()
img.image = UIImage(named: "tableview_loading")
return img
}()
// 提示语
private lazy var messageLable: UILabel = {
let lable = UILabel()
lable.text = "下拉刷新"
return lable
}()
private lazy var loadingView: UIActivityIndicatorView = UIActivityIndicatorView(activityIndicatorStyle: UIActivityIndicatorViewStyle.Gray)
}
控制器CNHomeTableViewController, 只需要做懒加载, 监听, 添加即可
// 下拉刷新
private lazy var refreshView: CNRefreshView = CNRefreshView()
// 添加下拉刷新视图
tableView.addSubview(refreshView)
// 监听refreshView,当refreshView是加载数据时调用loadData
refreshView.addTarget(self, action: "loadData", forControlEvents: UIControlEvents.ValueChanged)
// 停止下拉刷新
self.refreshView.endRefreshing()
代码如有哪里不妥可以加关注私信或评论给我, 谢谢