Pull to refresh is by far the most popular paradigm used in refreshing feed based views on an iOS device. First introduced by Loren Brichter of Tweetie, pull to refresh has made its way into just about every touch based device out there. Here’s the latest incarnation on the PS Vita (taken from @mattgemmell):
In fact, the idea was so good that Brichter decided to legally slap his name on it. Here’s a picture to help you better understand how this technology works (taken from gorumors.com):
Thankfully, Brichter doesn’t require you to obtain a license to use it shamelessly in your own app, bless him!
Now, our own Miso app also uses this familiar pattern to refresh our views. But the task of integrating a 3rd party library into our app was not as idiot proof as we had hoped. Here are some popular libraries and why they made me feel uncomfortable:
Both libraries are essentially the same, with slight variations. Both libraries employs the use of 3 absolutely crucial methods of UIScrollViewDelegate:
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView; - (void)scrollViewDidScroll:(UIScrollView *)scrollView; - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate;
In enormego’s implementation, it is expected that the developer chain the scrollview’s delegate methods to the EGOTableViewPullRefresh to drive the refresh cycle’s core logic. This isn’t ideal because it forces the developer to implement those delegate methods regardless of the developer’s requirements. This solution could definitely be more self-contained and less intrusive on the clients code.
In Culver’s implementation, the problem is the opposite of enormego’s. The delegate methods are literally stolen from you in order to drive the logic. If you implemented them yourself, the entire thing breaks.
One last variant is to have the library become the UIScrollView’s delegate, but also chain every single method back to the original delegate. This is fine, except it becomes extremely laborious to maintain especially if your UIScrollView is actually a UITableView (which extends the protocol UIScrollViewDelegate), or if apple adds some more delegate methods.
Both libraries combine the logic and views into one class, perhaps for the sake of simplicity. What if I didn’t want a boring rotating arrow but an animating rainbow? There’s basically no way to customize the views without modifying the source code directly.
Here’s my main beef with existing libraries:
- Too intrusive on my own code. I either play an essential part to making the library work, or the library will not work if I exercise some innocent freedoms.
- I couldn’t provide any custom views.
- I couldn’t provide any custom behaviors to the refresh cycle.
Instead of whining like I did, our very own CTO (Tim Lee) offered an actual solution to Beef #1: KVO (Key Value Observing). Specifically, to observe the contentOffset property of UIScrollViews.
[_scrollView addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld | NSKeyValueObservingOptionPrior context:NULL];
Using this trick, I was able to secretly offer pull to refresh features to any UIScrollView without having to become its delegate. With this simplification, I was able to quickly extend pull to refresh to all 4 edges of a UIScrollView, just in case you wanted to use the bottom side to load more, or if your feed was laid out in a carousel fashion.
Through the small exercise above, I saw MSPullToRefreshController as a self-contained object that handles nothing more except managing the logic involved pull to refresh cycles. It just didn’t make sense to add anything else to it, unless it enhanced the core behaviors. Translation: Beef #2′s solution doesn’t belong in this class.
To solve Beef #2 and #3 together, I decided to introduce the familiar delegate pattern to MSPullToRefreshController. It is through these methods that the developer will be able to customize behaviors and transform custom views to reflect each state in the refresh cycle.
Now I could easily write a wrapper around a MSPullToRefreshController instance and recreate any existing pull to refresh library by implementing 6 easy delegate methods! In fact, I’ve done just that in the included sample project on github!
Though this library is very simple, this marks my first step in to the world of open source (to reinforce our engineering culture per @nicolas). As @joshbuddy and @nesquena would say, 99% of open source projects stem from frustrations with existing solutions (or lack thereof). I hope somewhere someone will find this more suitable to his/her needs. Thanks for reading!