swift项目中对自定义上拉下拉刷新(对MJRefresh的封装)
我不知道官方自定义用法是怎么用的,看了下自己团队中项目的实现方式,觉得挺有意思的,特此记录一下 整体思路,从中间层下手,替换掉MJ_header方法,实现自己的header方法,同时使用自定义的headerrefreshview
好处是可以非常方便的在自定义的refreshview中添加自己所需要的动画或者其他视图
extra: 附带简单讲解下mjrefresh的逻辑
- MJRefresh基类MJRefreshComponent
定义刷新状态
定义子类需要覆盖的方法
**
重要的是对其父类添加了scrollview的位移监听,核心就在这里
监听到位移之后会通知子类去掉响应的事件
️**
- MJRefreshStateHeader继承自MJRefreshHeader
中间这一层MJRefreshHeader就不多说了,就是定义了一个header里面该有的东西,继承自MJRefreshComponent,同时在收到父类的位移回掉的时候回去调用自己的子类的方法
接下来是自定义时要使用的MJRefreshStateHeader,他继承自MJRefreshHeader
其具有刷新状态,可以更具刷新状态处理响应事件
- tableview.mj_header
平时调用的时候一般就是调用mj_header,传入一个header 和回掉
这个过程等下会模仿他重写掉,以此替换掉mj的mj_header方法,
转而使用我们自己的添加刷新方法
自定义实现
- 新建自己的header继承自MJRefreshStateHeader,重写 prepare
/** 写一个类继承自MJRefreshStateHeader, 我的取名叫GQRefreshHeader
把默认的时间label和状态label隐藏掉
*/
override func prepare() {
super.prepare()
self.lastUpdatedTimeLabel.isHidden = true
self.stateLabel.isHidden = true
self.mj_h = 56
}
- 重写placeSubviews方法,放上自己的动画view,这里用的lottie加载了一个刷新的圈圈
lazy var gifView: AnimationView? = {
let animationView = AnimationView(name: "refresh")
animationView.loopMode = .loop
animationView.isUserInteractionEnabled = false
addSubview(animationView)
return animationView
}()
override func placeSubviews() {
super.placeSubviews()
if self.gifView?.constraints.count ?? 0 > 0 {
return
}
self.gifView?.frame = CGRect.init(x: (self.bounds.size.width)/2, y: self.bounds.size.height/2, width: 24, height: 24)
}
- OC里面是重写setstate,swift里面就是把state属性的didset方法设置一下
/**
d
*/
override var state: GQRefreshHeader{
didSet {
guard let gifV = gifView else {
return
}
if state == .pulling || state == .refreshing{
gifV.stop()
gifV.play()
}else if state == .idle{
gifV.stop()
}
}
}
- 关键骚操作在这里
/**
扩展UIScrollView 添加一个 GQRefreshHeader类型的关联属性
*/
struct GQRefreshKey {
static var headerKey = 0
}
extension UIScrollView {
/// 关联属性 (扩展中添加)
private var gqHeader: GQRefreshHeader? {
get {
return objc_getAssociatedObject(self, &GQRefreshKey.headerKey) as? GQRefreshHeader
}
set {
objc_setAssociatedObject(self, &GQRefreshKey.headerKey, newValue,objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}
- 在该扩展中添加一个 addRefreshHeader 方法(mj 的setRefreshheader 原理也是一样的): 注意这一句 self.insertSubview 实际上就是把我们传进来的header 加到了图层里,
/// 添加下拉刷新时调用的方法
@discardableResult /// discardableResult目的是为了让返回值没被调用者接收到的时候不报警告(这句加不加无所谓 加了更优雅些)
func addRefreshHeader(refreshHeader:GQRefreshHeader?,refreshBlock:@escaping ()->Void) -> UIScrollView {
if gqHeader != refreshHeader {
gqHeader?.removeFromSuperview()
if let header: GQRefreshHeader = refreshHeader {
header.refreshingBlock = refreshBlock
self.insertSubview(header, at: 0)
self.gqHeader = header
}
}
return self
}
//触发头部上拉刷新
final public func triggerRefreshing(){
self.gqHeader?.beginRefreshing()
}
final public func triggerDelayRefreshing() {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.7) {
self.triggerRefreshing()
}
}
func endRefreshing() {
self.gqHeader?.endRefreshing()
}
6.外部调用的时候
self.tableView.addRefreshHeader(refreshHeader: GQRefreshHeader()) {
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()+2) {
self.tableView.endRefreshing()///模拟刷新完成后结束动画
}
}