Bookshelf View Controller Transition For iOS 7

Update: View Controller transitions are terribly broken in landscape. I’d advise avoiding this if you need anything other than portrait transitions.

Few will debate the divisiveness of the aesthetics of iOS 7, but what many seem to have only just started talking about is just how API rich this latest release is. I generally hesitate to write about brand new API (it’s just too hard to do it authoritatively without having spent real time with it) but the new flexibility in view controller transition animations is somewhat near and dear to Joel and I.

If you’ve spent any time in the past writing code to do custom animations between view controllers, you likely already know how challenging it was to keep code cleanly isolated; and even when proper encapsulation achieved, there was the tendency towards whole separate tracks of willDisplay/didDisplay logic in parallel with the standard UIViewController flow. The encapsulation of the new UIViewControllerAnimatedTransitioning mechanism is so clean and pluggable that I hope (and expect) to see transition animators popping up all over GitHub. We’d like to help kick off this trend. (skip right to the code)

If you haven’t read up on the new transition API, I recommend first taking a look at Ash Furrow‘s recent post, Custom UIViewController Transitions, it’s a fantastic nuts and bolts guide to quickly getting moving with custom transitions.

SMLBookshelfTransitionAnimator

SMLBookshelfTransitionAnimator provides an easy to use “bookshelf” animation between two view controllers, similar to the well known transition in iBooks. The bulk of the animation code was written by Joel, originally as part of a redesign of my very first (and very old) App Store app, Peg Jump. Truth be told, I’m not even sure this rotating 3D box is a style we’ll see much of in the post iOS 7 world, but it still fits in nicely with a heavily stylized game.

Admittedly, the Core Graphics animation to achieve this effect isn’t the most complex animation in the world. All the relevant views are added to a CATransformLayer with transforms applied such that they are positioned in the form of a “box”, the transform layer is then animated 180° in 3D space. The original code was not terribly elegant, it ripped the backing CALayer out of the UIView for the front and back controllers. This animated superbly, but required some terribly ugly post-animation cleanup. Fortunately, iOS 7 provides some great new view hierarchy rendering API and SMLBookshelfTransitionAnimator uses these to provide the textures for the various sides of the animated bookshelf; no more view hierarchy patching necessary.

Easy Setup

There’s some boilerplate setup required to get the transition animator passed into UIKit (read Ash Furrow’s blog post if you aren’t familiar), but setting up the animator couldn’t be simpler. SMLBookshelfTransitionAnimator provides sensible defaults, you really only need to provide it with views for the left and right sides.

SMLBookshelfTransitionAnimator *animator = [[SMLBookshelfTransitionAnimator alloc] init];
animator.leftView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"left.jpg"]];
animator.rightView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"right.jpg"]];
animator.dismissing = YES; // Required to automatically handle proper side view presentation

Options, we got em

SMLBookshelfTransitionAnimator allows customization of it’s duration, depth, perspective and rotation direction. Because we often create one off animator instances for presenting and dismissing view controllers, it also provides a couple of conveniences for automatically swapping the left and right sides to help aid in the appearance of a seamless, stateful, 3D transition.

As always, SMLBookshelfTransition is hosted over at the Spaceman Labs GitHub repo; and of course, feedback, suggestions, and pull requests are encouraged!

Posted in Uncategorized | 4 Comments

Compares

ComparesIconBig News.

As you may know, in October of 2012 Jerry and I left our day jobs in order to start Spaceman Labs. Our goal was to leverage our extensive knowledge and category-spanning experience on Apple mobile devices to build apps bursting at the seams with wonder and delight. The time from then to now has been full of learning experiences, many of which we have blogged about. But today we take our first public step towards our stated goal, with the release of Compares.

Compares!

There are plenty of ways to share a photo online, with your friends or perfect strangers. Whether filtered, auto-expiring, or creepily auto-identified, each picture is meant to tell a story larger than just the pixels that make it up. But some stories can only be told with two photos. Before and after. Compare and contrast. Young and old. We decided the world needed a photo sharing app perfectly designed specifically for side-by-side shots. From an onion-skinning camera to intelligently linked scaling and panning, Compares is just that. And since these stories are always made to be shared, we built a social infrastructure so you can connect with friends (from Facebook and the rest of life too) to see the compares they’re sharing. We’re even featuring the best of the best so you can find new people to follow.

IMG_0344

We’ve learned a lot in the process of making this app. We wrote our own backend, wireframed each screen, and controlled every aspect of the app from start to finish. This is exciting, and will spawn many blog posts in the future.

But for now, get the app and try it out! You can download it from the App Store. And you can see our (minimal) web client at compar.es. We’d love to hear what you think, which is why we put a “contact us” button in the settings. But if what you think is positive, we’d love even more if you’d rate and review it on the App Store.

Enjoy!

Posted in Software | Tagged , , , | Leave a comment

How To Support Old iOS SDK’s in Xcode 5

OH NOES, APPLE REMOVED SUPPORT FOR IOS 6 IN XCODE 5 *FIST SHAKE* !!!!

This is a sentiment I’ve seen a on twitter an awful lot in the last few days, and it’s not exactly accurate. The truth is, Apple does tend to encourage developers to link their applications against the latest SDKs; however, it is possible, with a tiny amount of hoop jumping, to use the new Xcode while linking against an SDK older than iOS 7.0.

The solution is simply to copy the old SDKs from the old Xcode to Xcode 5. Restart Xcode 5, and be sure you’ve changed your base SDK from Latest iOS to the specific SDK you’d like to build against, and you’re all set!

The iOS SDKs live here:

/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs

The Mac SDKs here:

/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs

If you’ve already blasted your old Xcode in exchange for the shiny new Xcode 5, there’s no need to fret. As of now, Xcode 4.6.3 is still the version in the Mac App Store, and provides the iOS 6.1 SDK. Just go download it, and grab the SDKs out of it before reinstalling Xcode 5.

Update: @rekle points out that it’s possible to get older versions of Xcode from the Downloads area of the Apple Dev Center. Even once Xcode 5 hits the App Store, it should still be possible to get an old SDK version.

Posted in Software | Tagged , , , | 11 Comments

SMSquashView

Why Now? Why Me?

Lately we’ve been hearing a lot about adding personality and interest to apps with subtle animation, rather than with complex textures. As an animation guy from old times*, I couldn’t be happier about this new focus in iOS. There have always been lots of rich animation capabilities built into the frameworks, and based on nothing but the WWDC keynote, there’s bound to be even more simple, powerful animation API in iOS 7.

Of course, there’s still plenty that’s not easy. In the 2.5d world of iOS 7, things like SMShadowedLayer are more necessary than ever. Dimensionality is one way to add, if I may, depth to your animations. Another way is to infuse character and realism into the animated object.

Take a look at the principles of animation. Many of these – slow in and slow out, anticipation, timing – are provided by existing iOS animation APIs. More – arcs, follow through – are likely to be provided by iOS 7 APIs. But the most important item on the list, squash and stretch, is not addressed. This is a shame, as it’s a remarkably versatile technique. Used with a delicate touch, it can help the eye follow animation, making it easier to tell where an object is heading and at what rate. Used more liberally, it can add an appealing playfulness and character to the same animation.

I Fixed It For You.

SMSquashView and its backing class, SMSquashLayer, squash and stretch themselves automatically in response to motion. They will distort themselves longer in the direction of motion, and shorter in the perpendicular directions. The distortion is proportional to the velocity the object, and scaled such that its volume remains constant. In short: it does exactly what you’d want it to do, with no configuration required.

How Does It Work?

The math is pretty simple, if you like transformation matrices. Every time the layer’s position changes, we note the time and position delta from last time. Using that information we can calculate the velocity for the current frame of animation. We align a transformation matrix to the direction of travel using the same idea as gluLookAt, then scale according to the magnitude of the velocity.

Animation

Much the same as SMShadowedLayer, we won’t get enough information during implicit animations. Accordingly, we use exactly the same technique: set up a custom internal animatable property, tell the layer it needs to redraw when that property changes, but sneakily do our math rather than drawing when display is called.

Demo

Where Can I Get It?

Here. There’s lots of really interesting open source code on our Github account, so you might want to check out the rest while you’re at it. This code in particular is short and quite well-commented (I think!) and might make a fun read.

 

*I argued for exactly this use of animation in my 2008 WWDC session. Prescient! I am willing to speak about the future of graphics and animation at your conference too, just ask.

Posted in Code, Software | Tagged , , , , , , , , , , | 5 Comments

WWDC Blast

Early this morning we and our friend Alex C. Schaefer launched a new project of special interest to iOS and Mac developers. WWDC Blast is a free service to inform you via SMS the very moment that WWDC tickets go on sale. With tickets having sold out last year in under two hours, there’s no better time to automate this. We won’t spam you, we won’t sell your information, we just like the idea of keeping people as informed as possible. Check it out.

Technically, this project was a fun one. It’s the first web app we’ve launched, and while the guts aren’t incredibly complicated, we’re integrating lots of cool services and frameworks to make some magic happen. We’re using Heroku, Twilio, S3, Twitter Bootstrap, MixPanel, and some other cool stuff that isn’t even user-visible yet. And the list goes on.

Most fun of all is that we put together the whole thing, soup to nuts, concept to launch, in under four days. That’s right: the initial idea was broached Monday morning. We launched early early Friday.

Not bad.

Posted in Software | Tagged , , , , , , , | Leave a comment

Spatter and Spark

icon_150

Another year-and-a-bit, another successful client project! As of a few weeks ago, Polk Street Press shipped a brand-new storybook, Spatter and Spark. It’s a genuinely endearing tale of two best friends solving problems together in a way that five-year-olds will find endlessly entertaining. We do too. The story is engaging, the character design and illustration is phenomenal, and there’s just a ton of incidental interactivity to keep the experience fresh and engaging read after read. There are also IAP games and activities designed to teach a preschool skill set – they happen to be pretty fun, too. In a very cool innovation, the activities, and some in-story data, are optionally parsed on PSP’s backend to give parents an auto-updating report on their child’s educational progress.

Most importantly from the perspective of this blog, Spaceman Labs is responsible for all the code in the iOS app. We built on all the work we’d done for Goodnight Safari, and were able to make use of a lot of clever stuff we’d done, like cancelable blocks, audio-synced text highlighting, and animated character encapsulation. But we also developed the animation framework significantly. As it’s now Polk Street Press’s IP we can’t tell you too much about it, but we can say it delivers greatly improved fidelity, performance, and memory utilization (yes, more than just fixing leaks), all of which enables much more detailed, expressive, and long-running animations in the same app size and on the same hardware. We also did a lot of work on moving animation specifications out of code and into artist-maintainable XML, which allowed for rapid iteration without our involvement.

We’re really proud of this project. We love the work we’ve done, and we love the finished product. So far reviews on iTunes seem to agree. Check it out (on the App Store and at Polk Street Press’s website) and let us know what you think.

Posted in Software | Tagged , , , , | Leave a comment

SMPageControl, Meet UIAccessibility

A while back we introduced SMPageControl, our drop in replacement for UIPageControl with a slew of extra bells and whistles. It was surprisingly well received, and is by far the most popular bit of open source code we’ve released to date. (Thanks for starring the repo on GitHub!)

Unfortunately, it seems that deeming it a “drop in replacement” wasn’t 100% accurate; while SMPageControl provided a pixel perfect representation of UIPageControl, it lacked UIAccessibility support all together. I hate to admit it, but until recently I haven’t paid much attention to accessibility in the iOS apps I’ve worked on. That has changed – for a variety of reasons that I plan to cover in a future blog post – and SMPageControl now really is a drop in replacement, providing the exact same UIAccessibility behaviors as UIPageControl.

But Wait, There’s More!

In sticking with the theme of being UIPageControl’s Fancy One-Upping Cousin, SMPageControl extends UIPageControl’s accessibility functions by letting you give each page its own name. The default behavior is to set the accessibility value to "page [current page + 1] of [number of pages]". e.g. "page 1 of 10". If a page has been named, the provided name is prepended to the default accessibility value. e.g "Search - page 1 of 10". This is extremely useful when using per page indicator images, where one or more page is likely to have a specific usage that is identifiable to the user.

Example

SMPageControl *pageControl = [[SMPageControl alloc] init];
[pageControl setImage:[UIImage imageNamed:@"searchDot"] forPage:0];
[pageControl setCurrentImage:[UIImage imageNamed:@"currentSearchDot"] forPage:0];
[pageControl setImage:[UIImage imageNamed:@"appleDot"] forPage:1];
[pageControl setCurrentImage:[UIImage imageNamed:@"currentAppleDot"] forPage:1];
[pageControl setName:@"Search" forPage:0];
[pageControl setName:@"Apple" forPage:1];

SMPageControl-3

Version 1.0 and CocoaPods

With the addition of accessibility support, I feel comfortable calling SMPageControl a 1.0; bugs may still surface, and features may be added, but the goal of creating a fully featured page control has been achieved. Having reached this point, I’ve created a 1.0 version tag in the git repo and added it to CocoaPods.

As always, feedback is welcome. We accept pull requests for features and bug fixes at the GitHub repo, so if you’d like to contribute, by all means, please do.

Posted in Uncategorized | Tagged , , , , | Leave a comment

Integrating Asana and Git

Recently we at Spaceman Labs have been testing out different tools and workflows to manage our various projects. After trying a few different packages, we’ve more or less settled on Asana as our issue tracking solution. It’s got a lot of advantages: it’s free, we can have as many projects as we want, and it’s low-friction enough that we’re likely to use it regularly.

That last one is actually the biggest feature – just as the best camera is the one you have with you, the best issue tracker is the one you’ll actually use. All the bells and whistles in the world don’t matter if you’re scared away from the core functionality. But there’s one way Asana could be even easier to use. It doesn’t have a standard way to integrate with our Git workflow. We’d like to be able to commit some code with a comment like “fixes #123” and have that fix reflected in Asana.

Fortunately, all of the groundwork has been done. Asana has a really awesome, and beautifully well-documented, API. And other developers, most notably for our purposes Bruno Costa, have taken that API and put together useful scripts.

The only thing missing from Bruno’s post-commit hook (and go back and read his blog post if you haven’t, it’s great) is the ability to run it non-interactively. Our Git workflow is built around Tower; it’s an awesome tool, but it doesn’t know how to simulate reading from /dev/tty in a hook.

So we needed to make the script non-interactive. This means it loses some potential flexibility, but on the other hand, it’s faster and has a somewhat higher “just works” quotient. Once you set up your git config variables, anyway. While we were at it, we also made it handle multiple ticket numbers per checkin, and differentiate between referencing and closing a ticket. That means you can write a commit message like

"Tweaked the widget; fixed #1, #2, and #3; references #4 and #5; oh yeah, and closes #6"

and have the right thing happen. Neat, right?

You can find the script at our GitHub page; please modify and open pull requests if you’d like to see it do more. (For instance, re-opening a closed ticket would be trivial.) V1 of the script is duplicated below. To run it, save it to your local repo’s .git/hooks/post-commit, and chmod 755 that mother. Run the git config line at the top of the script with the appropriate values, and you’re good to go.

#!/bin/bash

# modified from http://brunohq.com/journal/speed-project-git-hook-for-asana/

# -----------------------
# necessary configuration:
# git config --global user.asana-key "MY_ASANA_API_KEY" (http://app.asana.com/-/account_api)
# -----------------------

apikey=$(git config user.asana-key)
if [ $apikey == '' ] ; then exit 0; fi

# hold the closed ticket numbers
declare -a closed
# hold the ticket numbers that are not closed, just touched
declare -a referenced
# track whether we're currently closing tickets or just referencing them
closes='NO'

# regex pattern to recognize a story number
taskid_pattern='#([0-9]*)'
# regex pattern to recognize a "closing this ticket" word
closes_pattern='([Ff]ix|[Cc]lose|[Cc]losing)'
# regex pattern to recognize an "and" word (eg "fixes #1, #2, and #3")
and_pattern='([Aa]nd|&)'

# get the checkin comment for parsing
comment=$(git log --pretty=oneline -n1)

# break the commit comment down into words
IFS=' ' read -a words <<< "$comment"

for element in "${words[@]}"
do
    # if we have a task id, save it to the appropriate array
    if [[ $element =~ $taskid_pattern ]]; then
	if [ "${closes}" == "YES" ]; then
	    closed=("${closed[@]}" "${BASH_REMATCH[1]}")
	fi
	referenced=("${referenced[@]}" "${BASH_REMATCH[1]}")
    # or else if we have a "closes" word, set the tracking bool accordingly
    elif [[ $element =~ $closes_pattern ]]; then
	closes='YES'
    # and if we don't, set us back to referencing
    # (if we're an "and", don't change any state)
    elif [[ ! $element =~ $and_pattern ]]; then
	closes='NO'
    fi
done

# touch the stories we've referenced
for element in "${referenced[@]}"
do
    curl -u ${apikey}: https://app.asana.com/api/1.0/tasks/${element}/stories \
         -d "text=just committed ${comment/ / with message:%0A}" > /dev/null 2>&1
done

# close the tasks we've fixed
for element in "${closed[@]}"
do
    curl --request PUT -u ${apikey}: https://app.asana.com/api/1.0/tasks/${element} \
         -d "completed=true" > /dev/null 2>&1
done
Posted in Code | Tagged , , , , | 10 Comments

Mistakes Were Made: Audio and ARC

There’s a mistake I have, to my own great embarrassment, made twice, so I think it’s worth writing up for posterity. This one’s brief: you can laugh at me and move on.

Under ARC, AVAudioPlayers don’t retain themselves while playing.

A brief recap: ARC takes memory management off the minds of developers by automating the insertion of retains and releases according to a simple set of rules. This is much to the delight of new developers, and equally to the chagrin of grizzled oldsters. (I admit to having felt more than a bit of the latter at the outset.)

AVAudioPlayer is the simplest API to get an audio file from your Resources directory to your user’s ears.

AVAudioPlayer *player = [[AVAudioPlayer alloc] initWithContentsOfURL:audioPath error:nil];
[player play];

And you’re set! Under manual memory management, anyway.

What happens with ARC?  The newly created player is retained. As soon as the method ends, however, there is no longer anything referring to it. Even though it’s still doing stuff — the audio file you’re playing will presumably last longer than a millisecond — it’s released, and happily cuts off the audio early.

Having reasoned through it, the solution is of course trivial. Make the AVAudioPlayer an instance variable of whatever class is creating it, and it won’t be released until the controlling object is. Audio bliss.

Of course, I feel that the mistake is perhaps not entirely mine. AVAudioPlayer really should retain itself while it’s playing audio, and release itself when it finishes. To that end, I’ve filed rdar://12635205; feel free to dupe it if you agree. And for general interest, here’s the sample code I included to demonstrate the problem:

#define IVAR_PLAYER 0

#import "SMViewController.h"
#import <AVFoundation/AVFoundation.h>

@interface SMViewController ()
{
#if IVAR_PLAYER
	AVAudioPlayer *player;
#endif
}

@end

@implementation SMViewController

- (IBAction)playSound:(id)sender {
#if !IVAR_PLAYER
	AVAudioPlayer *player;
#endif
	NSURL *audioPath = [NSURL fileURLWithPath:[[NSBundle mainBundle]
											   pathForResource:@"audio" ofType:@"mp3"]];
	player = [[AVAudioPlayer alloc] initWithContentsOfURL:audioPath
													error:nil];
	[player play];
	
	// delay a bit to keep the run loop alive long enough to let the audio start playing
	// don't do this in production code, obviously! It is a bad way to do basically anything.
	for (int i = 0; i < 1000; i++)
		NSLog(@"%d", i);

@end
Posted in Explanation | Tagged , , , , , , , | Leave a comment

SMPageControl: UIPageControl’s Fancy One-Upping Cousin

If you’ve ever spent any time at Dribbble, you know how much designers love to customize UIPageControl, normally in the form of custom spacing or fancy inset looking page dots. As a developer, you’re probably also keenly aware of the lack of customization that Apple’s built in class provides. Hell, it wasn’t even until the introduction of iOS 6 that you could change the tint color, and even then, that’s where the flexibility ends.

Given my blogging absence in recent months, and the fact that I have a somewhat immediate need for a highly customizable page control, it seemed like a fun opportunity to spend an afternoon making something I could share. Admittedly, this isn’t a terribly hard problem to solve – UIPageControl is one of the least sophisticated bit of UI in iOS. Despite this being an open-source softball, I was excited by the idea of writing something simple, yet robust enough that I wouldn’t have to tackle this issue ever again.

SMPageControl provides all the functionality of UIPageControl, but also adds the ability to change indicator diameters, margins and alignment. It also provides support for using images as the inactive and active page indicator. For greater control, it is also possible to provide index specific indicator images.

If you’re not interested in the fanfare and hot air about to follow, go ahead and just grab the code from GitHub.

Update: Two new features have been added. A masking mode that will allow indicators to use the tint coloring, along with an image as a clipping mask. And a convenience method for updating the current page by passing in a scrollview.

Find and Replace

Using this class is as simple as they come, you can quite literally just change your class names from UIPageControl to SMPageControl, and SMPageControl will seamlessly provide all of the bland, out of the box functionality you are used to. It also provides the same two (and more) UIAppearance properties, pageIndicatorTintColor and pageIndicatorTintColor. I’ve made an effort to precisely mirror the default functionality, so that UIPageControl can be used as normal, while being able to make a quick shift to SMPageControl as soon as extra flexibility is required.

Example:

// Positioning code suppressed
UIPageControl *pageControl = [[UIPageControl alloc] init];
pageControl.numberOfPages = 10;
[pageControl sizeToFit];
[self.view addSubview:pageControl];

SMPageControl *pageControl2 = [[SMPageControl alloc] init];
pageControl2.numberOfPages = 10;
[pageControl2 sizeToFit];
[self.view addSubview:pageControl2];

A Little Bit Retro, a Little Bit Rock and roll

SMPageControl allows for the tried and true page control appearance, with some slightly adjusted layout. Using the fully UIAppearance compatible properties, indicatorDiameter and indicatorMargin, it’s easy to make SMPageControl appear a bit more modern.

// All instances of SMPageControl across the app will inherit this style
SMPageControl *appearance = [SMPageControl appearance];
appearance.indicatorDiameter = 10.0f;
appearance.indicatorMargin = 20.0f;

Alright, so bigger, more whitespace; that’s hardly Dribbble worthy, right? Well fortunately, SMPageControl has two more powerful properties – Also UIAppearance compatible – pageIndicatorImage and currentPageIndicatorImage. These are probably my favorite two properties, as they allow for a wholesale replacement of the active and inactive “dots”, with two quick and easy lines of code.

// All instances of SMPageControl across the app will inherit this style
SMPageControl *appearance = [SMPageControl appearance];
appearance.pageIndicatorImage = [UIImage imageNamed:@"pageDot"];
appearance.currentPageIndicatorImage = [UIImage imageNamed:@"currentPageDot"];

Stand Out In A Crowd

The final bit of customization provided by SMPageControl is its support for per-indicator images, a function that really is the icing of its flexibility. The easiest example of this to point out is the all too familiar Springboard. Ever since Spotlight was added in iPhone OS 3.0, the springboard page control has included a little search icon along with those classic circles. SMPageControl makes this sort of customization trivial. Because of the case by case nature of how I expect this feature to be implemented, per-indicator images are not compatible with UIAppearance – but adding them is just as quick and easy.

SMPageControl *pageControl = [[SMPageControl alloc] init];
pageControl.numberOfPages = 10;
[pageControl setImage:[UIImage imageNamed:@"searchDot"] forPage:0];
[pageControl setCurrentImage:[UIImage imageNamed:@"currentSearchDot"] forPage:0];
[pageControl setImage:[UIImage imageNamed:@"appleDot"] forPage:3];
[pageControl setCurrentImage:[UIImage imageNamed:@"currentAppleDot"] forPage:3];
[pageControl sizeToFit];

Feedback and Suggestions Welcome

I suspect that for a problem with such a narrow set of requirements, there isn’t much left that this little project doesn’t cover. That seems to be further supported by the similar feature set of this project and the others I’ve found. (P.S. It’s generally best to do the diligent Googling before writing your own new code to solve solved problems – whoops). However, I already mentioned that I wanted this to be robust enough that I wouldn’t have to chase a similar solution ever again. If there’s something I’ve missed, fill out the comment box just below.

If you’ve read this far, I’m impressed – and it’s probably time you just went and grabbed the code for a bit of tinkering.

Posted in Code | Tagged , , , , , | 13 Comments