Latest 2.1.0
License MIT
Platforms ios 8.0, requires ARC


ZVRefreshing is a pure-swift and wieldy refresh component.



  • iOS 9.0+
  • Swift 4.2



CocoaPods is a dependency manager for Swift and Objective-C Cocoa projects.

You can install Cocoapod with the following command

$ sudo gem install cocoapods

To integrate ZVRefreshing into your project using CocoaPods, specify it into your Podfile

source ''
platform :ios, '8.0'

target 'TargetName' do
    pod 'ZVRefreshing' ~> '2.0.0'

Then,install your dependencies with CocoaPods.

$ pod install


Carthage is intended to be the simplest way to add frameworks to your application.

You can install Carthage with Homebrew using following command:

$ brew update
$ brew install carthage

To integrate ZVRefreshing into your project using Carthage, specify it into your Cartfile

github "zevwings/ZVRefreshing" ~> 0.0.1

Then,build the framework with Carthage
using carthage update and drag ZVRefreshing.framework into your project.


The framework is under the Carthage/Build, and you should drag it into Target -> Genral -> Embedded Binaries


Download this project, And drag ZRefreshing.xcodeproj into your own project.

In your target’s General tab, click the ’+’ button under Embedded Binaries

Select the ZRefreshing.framework to Add to your platform.



You can use online demo on Appetize

Genaral Usage

When you need add a refresh widget, you can use import ZVRefreshing


There is three ways to initialize this widget.

  • Target-Action
let header = ZVRefreshNormalHeader(target: NSObject, action: Selector)
self.tableView.header = header
  • Block
let header = ZVRefreshNormalHeader(refreshHandler: { 
    // your codes    
self.tableView.header = header
  • None-parameters
let header = RefreshHeader()
self.tableView.header = header

if you initialize the widget by none-parameters way, you can add refresh handler block or target-action with following code:

  1. add a refresh handler
    // add refresh handler
    header?.refreshHandler = {
    // your codes            
  2. add a Target-Action
    // add refresh target-action
    header?.addTarget(Any?, action: Selector)
  3. add a Target-Action-UIControlEvents.valueChanged
    // The ZVRefreshComponent extend from UIControl, When isRefreshing properties changed will send a UIControlEvents.valueChanged event.
    header?.addTarget(Any, action: Selector, for: .valueChanged)


The functions is same for header and footer.

  1. beginRefreshing()

The widget begin enter into refreshing status.

  1. endRefreshing()

The widge begin enter into idle status.

  1. setTitle(_:forState:)
    To custom the title for widget, this function in ZVRefreshStateHeader.
header.setTitle("pull to refresh...", forState: .idle)
header.setTitle("release to refresh...", forState: .pulling)
header.setTitle("loading...", forState: .refreshing)


 footer.setTitle("pull to refresh...", forState: .idle)
 footer.setTitle("release to refresh...", forState: .pulling)
 footer.setTitle("loading...", forState: .refreshing)
 footer.setTitle("no more data", forState: .noMoreData)
  1. setImages(_:forState:)
    To custom the images for widget, this function in ZVRefreshAnimationHeader, you can use it as following code, also you can extend a subclass, like Example
self.setImages(idleImages, forState: .idle)
self.setImages(refreshingImages, forState: .pulling)
self.setImages(refreshingImages, forState: .refreshing)



  1. lastUpdatedTimeKey
    To storage the last time using this widget, if it dose not set, all your widget will shared a key com.zevwings.refreshing.lastUpdateTime
header.lastUpdatedTimeKey = "custom last updated key"
  1. ignoredScrollViewContentInsetTop

when your table set contentInset property, you should set it, for example:

self.tableView.contentInset = UIEdgeInsets(top: 30, left: 0, bottom:0, right: 0)
header.ignoredScrollViewContentInsetTop = 30
  1. lastUpdatedTimeLabel

To custom the UILabel properties for lastUpdatedTimeLabel, for example:

// hide the lastUpdatedTimeLabel
header.lastUpdatedTimeLabel.isHidden = true


// set the font for lastUpdatedTimeLabel
header.lastUpdatedTimeLabel.font = .systemFont(ofSize: 16.0)
  1. lastUpdatedTimeLabelText

To custom the format for showing last time.

header.lastUpdatedTimeLabelText = { date in

    if let d = date {
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
        return "Last updated:(formatter.string(from: d))"
    return "There is no record"


  1. isAutomaticallyHidden
    To set the automatically hidden for widget, default is true
footer.isAutomaticallyHidden = false
  1. ignoredScrollViewContentInsetBottom
    when your table set contentInset property, you should set it, for example:
self.tableView.contentInset = UIEdgeInsets(top:0, left: 0, bottom:30, right: 0)
footer.ignoredScrollViewContentInsetBottom = 30
  1. isAutomaticallyRefresh
    To set the automatically refresh for widget, default is true, this property in ZVRefreshAutoFooter
footer.isAutomaticallyRefresh = false


The following properties is same for header and footer.

  1. labelInsetLeft
    To set the empty width between activityIndicator an label.
header.labelInsetLeft = 32.0
  1. activityIndicator
    To custom the properties for activityIndicator, the properties @see ZActivityIndicatorView

  2. tintColor
    To custom the color for all sub-widget.
header.tintColor = .black
  1. stateLabel
    To custom the UILabel properties for stateLabel, for example:
// hide the stateLabel
header.stateLabel.isHidden = true


// set the font for stateLabel
header.stateLabel.font = .systemFont(ofSize: 16.0)
  1. animationView

To custom the UIImageView properties for stateLabel, for example:

Custom Usage

You can extend ZVRefreshComponent or it’s sub-class to custom your own refresh widget.
like Example.


  1. state

To custom you needed when refresh state changed.

open var state: ZVRefreshComponent.State
  1. pullingPercent

To custom you needed when widget position changed.

open var pullingPercent: CGFloat
  1. tintColor

To custom you own widget color.

open override var tintColor: UIColor!


  1. prepare

To define your own controls, call at init(frame: CGRect).

open func prepare() {}
  1. placeSubViews

To set your own constrols size and position, call at layoutSubviews().

open func placeSubViews() {}
  1. scrollViewContentOffsetDidChanged

To observe the UIScrollView.contentOffset, call at UIScrollView.contentOffset value changed.

open func scrollViewContentOffsetDidChanged(_ change: [NSKeyValueChangeKey: Any]?) {}
  1. scrollViewContentSizeDidChanged

To observe the UIScrollView.contentSize, call at UIScrollView.contentSize value changed.

open func scrollViewContentSizeDidChanged(_ change: [NSKeyValueChangeKey: Any]?) {}
  1. scrollViewPanStateDidChanged

To observe the UIScrollView.panGestureRecognizer.state, call at UIScrollView.panGestureRecognizer.state value changed.

open func scrollViewPanStateDidChanged(_ change: [NSKeyValueChangeKey: Any]?) {}

Update Log


This version convert to swift 4.0, add @objc to the functions, The new swift 4.0 feature will update next version.

V1.1 Why use UIControl?

The ZVRefrhing v1.0 is base on UIView, but it is not support for RxSwift binding, if the control beginRefreshing, you can use refreshHandler or addTarget(Any?, action: Selector), but it not a good idea for Rx.
Now, you can use ZVRefreshing with Rx, like:

    .map { [unowned self] _ in
        return self.tableView.footer?.isRefreshing ?? false
    }.filter {
        $0 == true
    }.subscribe(onNext: { [unowned self] _ in
        DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
    }).disposed(by: _disposeBag);

There is a Simple Demo for Rx.

Issue or Suggestion

You can issue me on GitHub or send a email[email protected].
If you have a good idea, tell me.


ZVRefreshing distributed under the terms and conditions of the MIT License.

Latest podspec

    "name": "ZVRefreshing",
    "version": "2.1.0",
    "summary": "A pure-swift and wieldy refresh component.",
    "description": "ZRefreshing is a pure-swift and wieldy refresh component.",
    "homepage": "",
    "license": "MIT",
    "authors": {
        "zevwings": "[email protected]"
    "platforms": {
        "ios": "8.0"
    "source": {
        "git": "",
        "tag": "2.1.0"
    "resources": "ZVRefreshing/Resource/*",
    "requires_arc": true,
    "default_subspecs": "Core",
    "subspecs": [
            "name": "Core",
            "source_files": [
            "dependencies": {
                "ZVActivityIndicatorView": []
            "name": "RxSwift",
            "source_files": [
            "dependencies": {
                "ZVRefreshing/Core": [],
                "RxSwift": [],
                "RxCocoa": []

Pin It on Pinterest

Share This