Tweetbot 5 came out recently, and it has a custom refresh control that looks really good. It’s a simple animation, but it gets the job done, so I decided to recreate it.

Note: When I started implementing this animation I thought about implementing it as a refresh control, but it quickly started feeling like work, so I decided to just focus on the animation. Anything else is left as an exercise to the reader.

Introduction

Just like my first article, Recreating Instagram’s Page Control, in this article I will recreate a UI component from an app, in this case it’s Tweetbot’s Refresh Bar.

This control is pretty simple, it’s a bar that fills as you drag down, and once you reach a certain distance, it starts bouncing left and right.

Download the Playground and follow along. It has a page for each step.

Step 1: Class Setup

This step is really simple, we’ll create a UIView subclass with a single UIView property that will be used as the fill.

We’ll use two existing properties of UIView to style our control, backgroundColor for the background (shocker!), and tintColor for the fill. That way we won’t need to add any extra properties, but we will need to override tintColorDidChange() to update the background color of our fillView .

Finally, we’ll override intrinsicContentSize and return the size we want our control to have. If you’re not familiar with what intrinsicContentSize does, you can read about it in this article I wrote.

Step 2: Progress Fill

Our view will have two states, the first one will be when it fills. Since the bar will fill as the user drags down, we’ll add an associated value to store that progress.

The second state will be when the bar is animating. To handle these two states we’ll create the following enum .

Next we’ll implement layoutSubviews , and use it to round the corners of our views, then we’ll call our updateFrames() function.

In our updateFrames() , first we’ll make sure that state is .progress , otherwise we exit early. Then we’ll use lerp to calculate the width of our fill view, and set it’s frame. The lerp function is found in my CGMath library, you can learn more about it in the readme.

Step 3: Animation

To start implementing the animation we’ll need a couple of constants, the width of the bar and the duration of each step of the animation.

We’ll split the animation in two very similar steps, each step will use UIView.animate to set the frame of our fill view, and on completion, if state is still .loading , we’ll call the other step, this will cause the animation to loop endlessly.

Now, in state.didSet we’ll call animateFirstStep() if state is .loading . If state is not .loading , we’ll check if it previously was, and if that’s the case, we’ll animate the call to updateFrames() .

Step 4: Final Touches

To get it production-ready, we’ll turn loadingWidth and duration from private constants to variables.

We’ll make also RefreshBar.State conform to Equatable , and use that to exit early if state is set to the same value.

And done!

This animated refresh control was quite easy to implement, the trickier part would probably be to integrate it into a scroll view, but as I mentioned before, that is an exercise that will be left to the reader.

Stay tuned for my next article about Instagram’s story loading animation!