Thomas Denney

Building Keep Calm 4

A couple of days ago I released Keep Calm 4 for iOS, just over two years after the release of the first version. The new update brings several bug fixes as well as a few new niceties. Despite the bump in the version number this has probably been one of the smallest updates I’ve done to the app since its release. In this post I’m going to give a technical overview of some of the work I’ve done on the app.

View controller transitions

In iOS 7 Apple introduced View Controller transitions (see objc.io for a really good tutorial). These give you full control over how one view controller transitions to another. In the above video you can see that I am pushing from a UICollectionViewController to a custom view controller (alternatively I could have used a Collection View layout transition, but this would have been a bit inefficient for my use case). Towards the end of the video I swipe in from the left of the screen (i.e. the standard back gesture) and you can see that the poster interactively shrinks back into its original position. In order to achieve the effect I do the following:

  1. Set the navigationController delegate to the UICollectionViewController
  2. The UINavigationControllerDelegate methods return instances of KCCInteractiveTransiton, which handle the back and forward transitions. KCCInteractiveTransition is a subclass of UIPercentDrivenInteractiveTransition and implements UIViewControllerAnimatedTransitioning
  3. All of the collection view cells undergo an animation that shrinks them (sets their transform to a scale of 0.75) and fades them out
  4. The new view controller is immediately added to the view, along with the main poster view
  5. The poster view is positioned in the same place as the cell that was tapped
  6. The poster view then undergoes an animation that causes it to fill the screen

In reverse a similar series of events happens, except that it also supports interaction (so you can effectively watch the animation in slow motion). I’ve also used some of the spring based animations in iOS 7 to give it a little bouncing effect. The following values produce a fairly subtle bounce:

[UIView animateWithDuration:0.5f delay:0 usingSpringWithDamping:0.9 initialSpringVelocity:15 options:UIViewAnimationOptionCurveEaseInOut animations:^{
    ...
}];

It wasn’t particularly trivial to implement the interactive transition, and it was made harder by the fact that I’m using Auto Layout pretty much everywhere in Keep Calm now. Auto Layout animations also work slightly differently to regular animations as well, because you don’t set the final state of the animation inside the animation block:

//Configure constraints for 'final' animation state
[UIView animateWithDuration:duration animations^{
    //'Apply' the updated constraints
    [self.view layoutIfNeeded];
}];

Vector crowns

Keep Calm contains 1000 custom crowns that you can replace the crown at the top of the poster with (as well as the option for images and emoji). In previous versions these have always been @2x PNG files and they took up about 70% of the app’s bundle size. This has been fine up until now, but with the @3x iPhone 6+ I really needed to improve the quality.

Now Keep Calm ships mostly with PNGs still but many of the more common crowns have been replaced with vector paths instead. These vectors have been produced by:

  1. Taking the original SVG file and simplifying it down to one path using Sketch
  2. Converting strokes to filled paths
  3. Exporting each crown as an SVG file
  4. Running a custom C program on each SVG file to produce binary encoded path data, which is then stored in an SQLite database
  5. Keep Calm loads the binary path data from SQLite and decodes it using a custom UIBezierPath extension
  6. Paths are rendered in a custom view using the appropriate colour

Core Image colorisation

As well as being able to change the crown in Keep Calm, you can also change the colour of it. In older versions of the app the white crown PNGs were filtered using a CIColorMatrix filter. Unfortunately, despite having the math right, the produced colour was always slightly different from the text. It was usually fine for grey scale colours, but anything else would be off by ±5%. I’m still yet to establish why this occurred, but there was an easy work around.

I now create a plain CIImage which represents a single colour. Then I apply a mask to that image using the alpha channel of the crown image. The result is a correctly coloured crown image.

This bug has been in Keep Calm for nearly two years, so I’m delighted to finally fix it once and for all.

Supporting iOS 7 and 8

Keep Calm 4.x will continue to support iOS 7 into the foreseeable future. I’m not depending on any iOS 8 features like extensions, so it makes sense to support iOS 7 still. Furthermore, given that iOS 8 adoption is slower than iOS 7 it will be a while until the majority of my customers are on iOS 8. A key factor I have to consider is that the vast majority of Keep Calm users tend to be children, so devices like the iPod touch and 16GB iPhones make up the majority of my users. These devices may not have been updated to iOS 8 in fears that it will slow them down or that it requires too much free storage space.

Swift

This update doesn’t contain any Swift because it was primarily written against the iOS 7 SDK in about July (I was slow releasing it because of the extra work of vectorizing crowns). I’m pretty sure that I will begin to use Swift more over the next few months, but for now I’m restricting it to where I’m creating new classes.

Future

Keep Calm on iOS remains a priority for me over the Android version. Over the next few months I’ll be adding more vector crowns and bug fixes. The next update (probably 4.1) will likely be a ‘Snow Leopard’ style release where changes are primarily just fixes and minor improvements rather than major new features.

About half of all of the code in Keep Calm is in my private shared framework that I use across my apps. I’ve finally begun cleaning it all up and putting it in separate modules, so it is actually reasonably likely that it will be fully open sourced in the next few months :).