In this tutorial we will create a YouTube play button animation using Facebook POP. The source code will be shared on Github. It will also include an alternative way of doing this animation using CoreAnimation.
To begin with, we need to create a new Single View Application project (File -> New -> Project). After project is created, we need to add CocoaPods. Add this pod and install it:
POP still does not have Swift support, so we have to create bridging header which allows us to user Swift and Objective-C in the same project:
- Create .h file (File -> New -> File -> Source -> Header File) and name it BridgingHeader;
- Go to target build settings and search for Objective-C Bridging Header;
- Provide path to your bridging header. It should be ProjectName/BridgingHeader.h
Add this import to your BridgingHeader.h:
Build the project to make sure that bridging header works properly and there are no warnings. It may fail because of enabled bitcode, as POP does not support it yet. To fix this error, you have to disable bitcode by going to your Target -> Build Settings -> Search for “bitcode”:
For animation we need to create a new UIButton and call it PlayButton.
As you have seen, this button will animate between two states: Paused and Playing. We need to define them using enum. Copy and paste this code above class declaration:
The reason why I added
var value: CGFloat will be described later.
Now I would like to go threw the theory of how button will animate between these two states.
POP allows us to create animatable property — and that is exactly what we are going to do. We will create CGFloat variable named
animationValue and will animate it from 1.0 to 0.0 when button state is changed from Paused to Playing, and animate from 0.0 to 1.0 when button state is changed from Playing to Paused. Every time value has changed we will call
setNeedsDisplay which will force our view to redraw.
I think, now is a good time to try it!
Okay, lets declare some variables! Put this code at the beginning of class declaration:
Now we have two variables. First one is
buttonState. This value can be accessed outside the class, but it can be set only inside the class. Second variable is
animationValue. As you can see, we call
didSet as it was explained above.
Next step is to create method which will be responsible for setting up animation or only updating
animationValue when animation is not needed. The only reason why I added
animated: Bool to this method is that if you will use this button in table view or collection view you will have to set state without animation when cell displayed.
- We return if
buttonStatehas not changed, and update
buttonStateif it has changed;
- We remove previous animation if it exists;
- We create immutable variable
toValueand set value of the
buttonStatewhich is calculated in
var value: CGFloat(Swift allows us to have very clean and nice implementation of vars in enumerations);
animated == true, then we initialise
POPAnimatableProperty. Otherwise we only set
Lets understand how we initialised
POPBasicAnimation in our example.
duration — everything is simple and does not require explanation, right? All these parameters can be used in CABasicAnimation. The only complex part of
POPAnimatableProperty. As we animate our own property, we have to initialise our own animation block which contains:
readBlock— reads values from a property and stores in an array of floats (CGFloat values);
writeBlock— writes values from array of floats into property;
threshold— defines the smallest increment of the value.
More information can be found here.
If we would like to animate built-in property it would be easier — we could just use
to animate view alpha. List of built-in properties can be found here.
Now we have everything we need to animate property and I would like to explain logic of animation, so we understand what to write in drawRect.
Assume, that we are going to animate from Paused to Playing (1 to 3). Our “triangle” button will split in two halves — trapeziums (2), and will animate them to rectangles. The easiest way to achieve this effect is to always have 2 geometrical figures consisting of four points:
Next step is to understand which values we need to calculate. I have prepared special image which describes all values very well:
minWidth— this value never changes, it defines minimum width of left and right halves (I like when it is equal to 32% of full button width);
aWidth— this value is calculated using animationValue. When animationValue = 1, this value will be zero. When animationValue = 0, this value will be equal to half of button width minus minimum width;
width— equals to minimum width plus additional width (minWidth + aWidth);
H1— padding from top to the left half top right point and the right half top left point, AND padding from bottom to the left half bottom right point and the right half bottom left point. When animationValue = 0, this value will be equal to height / 4. When animationValue = 1, this value will be equal to 0;
H2— padding from top to the right half top right point AND padding from bottom to the right half bottom right point. When animationValue = 0, this value will be equal to height / 2. When animationValue = 1, this value will be equal to 0.
Okay, I think it is enough theory and good time to start. Even if you did not understand something — please do not be scared, you will understand it once we override drawRect. Copy and paste this code in PlayButton:
This code is straightforward:
- We calculate all values as per image above. As I mentioned before, minWidth value is always the same, despite the fact that animationValue changes. I like when it is equal to 32 percent of the whole width. You can adjust this value as you want, but keep in mind, that it should be less or equal to half of width;
- As we are overriding drawRect, we should not to create a new context — we should get and use existing;
- Create CGPath of two trapezius, as it was described before;
- Set fill colour (I am using tintColor, feel free to change it).
The last step is to add
PlayButton to our
ViewController and test how it animates. Delete everything inside
ViewController class and paste this:
Now build your project and enjoy! This animation is unbreakable — you can tap button as quick as possible and it will always smoothly animate between two states without any visual breaks. Pretty good, isn’t it?
If you are excited about drawRect animations, please let me know, I will write second part of this tutorial with 2 more nice animations!
All source code from this tutorial PLUS CoreAnimation implementation can be found here.
Thank you for reading, I hope you enjoyed! Please let me know which animation would you use in your project — drawRect or CoreAnimation. Cannot wait to read your comments!