Flirting with people - iOS style
There was a girl I liked. I did not know how to draw, sing or say cute things to impress her. I decided to make something cute the only way I knew how - by doing animations on iOS. This is the result:
Drawing curves
To create an animation of two drawings like this, we'll be using Bézier curves. The API for this on iOS is UIBezierPath
. The UIBezierPath
object is a model object for storing the points, curves and lines of our drawing. To display this, we will be using a CALayer
subclass called CAShapeLayer
.
UIBezierPath *path = [[UIBezierPath alloc] init];
// add points to the path
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
shapeLayer.strokeColor = [UIColor magentaColor];
shapeLayer.fillColor = [[UIColor magentaColor] colorWithAlphaComponent:0.5f];
shapeLayer.path = path;
To install the shapeLayer
into the view hierarchy we either -addSublayer:
it to a view.layer
of any view or we create a wrapper UIView
where we subclass the +layerClass
method like this:
+(Class)layerClass {
return [CAShapeLayer class];
}
And then the self.layer
of the UIView
is guaranteed to be a CAShapeLayer
.
Animation
Unfortunately, while many CALayer
properties are implicitly animatable, the path
property isn't. We have to create our own CABasicAnimation
and parametrize it for the animation to take place.
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"path"];
animation.fromValue = heartPath.CGPath;
animation.toValue = textPath.CGPath;
[shapeLayer addAnimation:animation forKey:nil];
This code will animate the shapeLayer
once.
Interaction
To make the animation interactive we'll be using the CAMediaTiming
protocol. The documentation is very general here:
The CAMediaTiming protocol models a hierarchical timing system, with each object describing the mapping of time values from the object's parent to local time.
This protocol basically lets us play Chronos (or Sailor Pluto). We can set the speed of animations, e.g. speeding them up 5
times or making them not progress by themselves. We can also control the progress in animation with properties.
- (void)viewDidLoad {
[super viewDidLoad];
//rest
self.animation.duration = 1.0f;
self.animation.speed = 0.0f;
}
- (void)sliderValueChanged:(UISlider *)slider {
self.animation.timeOffset = slider.value
}
In this sample we use the previous UIBezierPath
animation
. We set the speed
to 0
, so it won't do anything by itself. We set the duration
to 1
so the possible values of slider.value
will map perfectly to animation progress.
We manually set the progress of the animation using timeOffset
using the slider
callback.
Better animation
There's another trick to make it better (don't mind the quality):
To make it look like this you have to remember to balance the number of segments in both UIBezierPaths
. Just make sure that the number of -addLineToPoint:
and
-addCurveToPoint:controlPoint1:controlPoint2:
for each UIBezierPath
is roughly equal. Then every segment of the first path will animate into a segment in the second path, creating a better quality animation.
The heart path in the first version had only 2 curves, whereas the word Nikoleta had a lot of them. We can see that the heart grows from the first segment of the first letter of the word.
Tools
To create those complex Bezier paths you can use an app that will generate them for you:
- Bezier Path Designer - I used this, but it's pretty time consuming to draw something complex.
- Drawscript
- PaintCode
Personal
The girl I did this for thought it was really cute. She is now my fiancée and we're happy together. I hope you will find your second half too.