Pull-down refresh of custom in swift project (encapsulation of MJRefresh)

I don’t know how the official custom usage is used, but I looked at the implementation of my team’s project and found it interesting. Here is a record of the overall idea: Start from the middle layer, replace the MJ_header method, implement your own header method, and use a custom headerRefreshView

The advantage is that you can easily add animations or other views to your custom RefreshView

Extra: With a brief explanation of the logic of MJRefresh

  • MJRefresh MJRefreshComponent base class
The most important thing is to add a scrollView shift listener to its parent class, where the core will notify the subclass to remove the event ️**Copy the code
  • MJRefreshStateHeader inherits from MJRefreshHeader
The middle layer MJRefreshHeader defines what should be in the header, inherits from the MJRefreshComponent, And the method to go back and call its own child class when it receives the parent class's displacement back is followed by the custom MJRefreshStateHeader, which inherits from MJRefreshStateHeader and has a refresh state, which can handle the response event in a refresh stateCopy the code
  • tableview.mj_header
The usual way to do this is to call mj_header, pass in a header and discard it, and we'll rewrite that later, replacing MJ's mj_header method with our own add refresh methodCopy the code

Custom implementation

  1. Create your own header inheriting from the MJRefreshStateHeader and rewrite prepare
/** Write a class that inherits from MJRefreshStateHeader. My name is GQRefreshHeader and hides the default time label and state labelprepare() {
        super.prepare()
        self.lastUpdatedTimeLabel.isHidden = true
        self.stateLabel.isHidden = true
        self.mj_h = 56
    }
Copy the code
  1. Override the placeSubviews method and place your own animated view, where Lottie loads a refreshed loop
lazy var gifView: AnimationView? = {
        let animationView = AnimationView(name: "refresh")
        animationView.loopMode = .loop
        animationView.isUserInteractionEnabled = false
        addSubview(animationView)
        return animationView
    }()
    
override func placeSubviews() {
        super.placeSubviews()
        ifself.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) }Copy the code
  1. OC is overriding setstate, swift is setting the didset method on the state property
/**
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()
            }
            
        }
    }
Copy the code
  1. The key operation is here
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)
        }
    }
  }
Copy the code
  1. Add an addRefreshHeader method to this extension (mj’s setRefreshheader works the same way) : Note that self.insertSubView actually adds the header we passed in to the layer,
/// discardableResult is designed to avoid warning when the value is not received by the caller addRefreshHeader(refreshHeader:GQRefreshHeader? ,refreshBlock:@escaping ()->Void) -> UIScrollView {ifgqHeader ! = refreshHeader { gqHeader? .removeFromSuperview()if let header: GQRefreshHeader = refreshHeader {
                header.refreshingBlock = refreshBlock
                self.insertSubview(header, at: 0)
                self.gqHeader = header
            }
        }
        returnSelf} // Trigger a header pull-up to refresh final public functriggerRefreshing(){ self.gqHeader? .beginRefreshing() } final public functriggerDelayRefreshing() {DispatchQueue. Main. AsyncAfter (deadline: now () + 0.7) {self. TriggerRefreshing ()}} funcendRefreshing() { self.gqHeader? .endRefreshing() }Copy the code

6. External calls

self.tableView.addRefreshHeader(refreshHeader: GQRefreshHeader()) { DispatchQueue.main.asyncAfter(deadline: DispatchTime. Now () + 2) {self. TableView. EndRefreshing refresh () / / / simulation after the end of the animation}}Copy the code