Fixing CircleLayout


CircleLayout is an Apple sample from WWDC 2012 (Session 219) that demonstrates a custom collection view layout and also custom animations when inserting and removing items from the collection.  The trouble is that in the time since this sample was published, the UICollectionView API has changed somewhat and the sample no longer works.

The project still compiles and runs (once you import the Quartz header in Cell.m), displays items in a circular layout, and you can insert or remove cells by tapping either off or on a cell, and the remaining cells animate to their new positions.  However, in the original sample, cells being removed shrunk and faded out to the center while cells being inserted moved out from the center while fading in, but that no longer happens.

The initial problem is that the 2 methods for providing the custom attributes for the inserted/removed cells has been renamed.  If you fix that, then you start getting custom animations, but something really bizarre happens: when you insert or remove a cell, all the cells shrink away to the center (before reappearing in their correct places).  What’s going on?

The answer is that initialLayoutAttributesForAppearingItemAtIndexPath: and finalLayoutAttributesForDisappearingItemAtIndexPath: are not just called for the inserted and removed cell(s), respectively.  They’re called for all visible cells; i.e. those that need to move as well as the one(s) being inserted or removed.  Not only that but initialLayoutAttributesForAppearingItemAtIndexPath: even gets called when removing an item and finalLayoutAttributesForDisappearingItemAtIndexPath: also gets called when inserting an item!

The Solution

prepareForCollectionViewUpdates: gets called before any insert/delete animations occur.  We can use that to record the indexPaths of those items being inserted or removed (don’t forget to call super).  Then in initialLayoutAttributesForAppearingItemAtIndexPath: we only need to modify the attributes for indexPaths that match the insert operations, and in finalLayoutAttributesForDisappearingItemAtIndexPath: we only need to modify the attributes for indexPaths that match the delete operations.  Again, don’t forget to call super because especially for initialLayoutAttributesForAppearingItemAtIndexPath: this returns non-nil values for the items being moved, and the remaining cells won’t animate their moves to their new positions without it.

I posted my corrected version of the CircleLayout project to GitHub, and if you’re interested you can check out the commit changes to CircleLayout.m to see what I changed to reinstate the custom animations.

UICollectionView is one of my favorite new bits of API in iOS 6.  I can’t wait to see how it develops and matures, and am eagerly awaiting my first iOS 6-only project so that I can put a collection view into production code.

Rotating an OpenGL view with touch gestures →

I wrote a tutorial over on Odyssey Computing’s site on using a UIScrollView to back the touch gestures for rotating an OpenGL view. This was something that came up in the context of a larger project that involved incorporating a GLKViewController as a component of a larger UIView-based interface. I love using UIScrollViews to provide pan gesture handling for custom (unconventional) interfaces. In this case I map the contentOffset property of the UIScrollView to the degree of rotation of the GLKViewController‘s model. The great thing about using UIScrollView is that you get deceleration that feels right for free (and where applicable, bouncing).

CocoaConf PDX 2012

I am proud to announce that I will be presenting at CocoaConf PDX in Portland, OR this October.  I’ll be debuting a new talk on something I can’t talk about quite yet.  But it’ll be about something new and fun to play with for you to create great interfaces for your users.

Update: Now that the NDA on iOS 6 has been dropped, I can reveal that my new talk will be on UICollectionViews.

Title: Introducing Collection Views

Abstract: UICollectionView, introduced in the iOS 6 SDK, is Apple’s new class for visualizing data in a grid or really any format other than a vertical list.  We’ll cover the basics and then explore the intricacies of UICollectionViewLayout,  UICollectionViewFlowLayout and related classes.  Along the way we’ll learn how to make both horizontal and vertical grids, cover flow, iPhoto-like stacks, and other custom layouts.  Apple has provided yet another tool that makes it easier and faster for you to provide rich experiences for your users – come learn how to hit the ground running with UICollectionView.  Plenty of source code will accompany the talk.

I’ll also be giving my matrix transformations talk, which now has even more rotational and graphical goodness.  That one is a longer session, so there will be plenty of time to get into all the minutiae of shadows, timing curves, anti-aliasing, rendering layers as bitmaps, etc.  If you’re interested in either FlipBoard-style page-flipping animations or Clear-style folding animations, then you won’t want to miss this session.

Title: Enter The Matrix: Reloaded

Abstract: Matrix transformations can make your user interfaces come to life: translate, scale, and rotate. Each on its own is relatively simple and straightforward. Yet many developers are daunted when 2 or more operations need to be combined. What if you need to rotate or zoom about an off-center (or even off-screen) point? How do you combine multiple transformations into a single animation? How do you make advanced, polished 3D animations such as folding and flipping views? Learn everything you need to know to get started with complex matrix transformations in CoreGraphics and CoreAnimation and take an in-depth look at folding and flipping animations. We’ll also cover related topics such as anti-aliasing, avoiding off-screen render passes, shadows, and rendering retina images. Tons of demos and full open-source source code provided.

I’m super excited to visit Portland and looking forward to Chris Adamson‘s Core Audio Workshop.  There’s even a place where you can get bacon on maple-frosted pastries.

Bacon maple goodness – so wrong and yet so right

Early Bird registration ends Sept 14th.

Tips for presenting at technical conferences

I want to present a few tips I have for presenting at technical conferences, specifically at iOS / Mac-focused technical conferences. This isn’t going to be a list of tips on speaking — you can go to Toastmasters for that — just a few things I’ve picked up or think are important.

Have your slides and code posted online by the start of your session

They have to be ready by then anyway, right? Right? So take a few extra minutes to make sure they’re uploaded someplace where they can be shared whether that’s GitHub, DropBox, or your website. There are plenty of free options available, so there are no excuses. How many times have you heard during a session, “I’ll post the slides/code soon” and then how often do you remember to go back and check later to download them?

Bring a few thumb drives with your slides/code on them

Many times conference WiFi is slow, flaky, or nonexistent. Having your materials available online doesn’t necessarily help if your attendees can’t access them immediately. Small capacity flash drives are super cheap nowadays, so buy a few and take a few minutes to copy over your slides and code just before the talk (you know, just after you finish finalizing them).  Attach ribbons or spray-paint them hot pink to help keep track of them.

Bring all your converters, adapters, cables, and chargers

Bring more than you think you’ll need. If the conference projector is VGA, then you’ll definitely want your mini DisplayPort to VGA adapter, but you should also bring your other adapters and maybe an HDMI cable just in case. And if you’re running any iOS apps and have either an iPhone 4S or an iPad 2 or newer, then bring adapters (VGA and/or HDMI) so that you can project directly from the device via AirPlay Mirroring if necessary. (You never know when Xcode might refuse to run your code in the Simulator.) The Apple Digital AV Adapter (HDMI) is especially useful as the only one that lets your do video out from your iOS device simultaneously while debugging or running Instruments from your MacBook over USB, so bring a USB sync cable too.  It goes without saying that you should have chargers for your MacBook and iOS devices as well.

Bonus tip: If you use a retina MacBook Pro or a new 2012 MacBook Air to present, remember to pack a MagSafe to MagSafe 2 converter so that you can charge your laptop using the power adapter that’s hopefully installed on the podium.

Have Contingency Plans

If your session relies on live-coding and/or executing sample code from Xcode, what will happen if Xcode refuses to cooperate with you?  Do you have the code already compiled on an iOS device (possibly multiple) and the cables necessary to present from it?  Do you have git branches or tags for all the major stages of your live-coding demo?  If all else fails, do you have slides showing screenshots of what would have happened?  Expect the unexpected and you will recover more quickly and gracefully when it inevitably happens.

Rev Your Talk

In an ideal world, every conference presentation would be a new original talk crafted specifically for that conference session.  But in reality, good presentations are incredibly time-intensive to create and this just isn’t possible.  I’ve been trying to debut 1 new talk for each conference, but even that may not be sustainable long-term.  Also, some talks are great and should be shared widely with different audiences, so in that sense, repeating them is perfectly acceptable.

However, you should still rev your talk with each presentation:

  • Review your slides and update / correct them as necessary.
  • Fix a bug or add a feature to your sample code.
  • Make a change based on feedback from the previous rendition of your talk.
  • Imagine that someone in your audience attended the previous version of your talk and ask yourself, “What new tidbit of knowledge will I include this time that she didn’t get last time?”
  • And run through your talk at least once, even if you’ve already given it a half dozen times before.

In Closing

Essentially all of the above tips are corollaries derived from the following 2 axioms:

  1. Be Prepared
  2. Respect Your Attendees and Value Their Time

As a presenter you have an obligation to provide value to your attendees.  In exchange for presenting you are receiving entry to the conference, probably accommodation, and possibly airfare and maybe even a speaker’s fee.  But more importantly you will be receiving recognition from your peers and a platform from which to promote yourself, your book, your app, your company, etc.  I know you’re busy with your day job, the next great app, your next book or a demanding high-profile consulting client.  So is everybody else.  You owe it to your audience to put in a modicum of effort (and preferably much more) into each conference appearance.

I encourage you to share your domain-specific knowledge and areas of expertise with the community, and I look forward to seeing you at a tech conference soon.

iOSDevCampDC wrap-up

Last Saturday I had the privilege of speaking at and attending iOSDevCamp DC (which actually took place in Reston, VA).  iOSDevCamp DC is a single day, single track conference that is now in its 4th year.  This was my 2nd year attending.  Unlike most conferences, this one groups all the attendees in a single (large) room for the day (stocked of course with plenty of beverages and snacks as well as breakfast and lunch).  This aspect really helps facilitate the mingling and conversations that make attending conferences so valuable.

I was the 4th of 6 speakers and I gave my matrix transformations talk for the 3rd time in 5 months.  This time I deliberately cut the introductory material somewhat and spent more time on folding and flipping animations plus touched on some general graphics tips like anti-aliasing, rendering retina bitmaps, and avoiding off-screen render passes.  I think it went well.  I forgot to sacrifice to the demo gods beforehand though and got the dreaded bootstrap server error that prevented me from running my demo app in the iOS Simulator.  Fortunately, I had it on my iPad and with the help of the HDMI adapter was able to run Instruments while projecting the iPad screen.

I enjoyed the other 5 talks, but especially Ken Yarmosh‘s talk on gestures and Jonathan Blocksom‘s iOS concurrency talk.  I think I picked up about 3 different nuggets from Jonathan’s talk alone that made attending worthwhile.

Slides from my “Enter The Matrix: Reloaded” talk can be found here (latest version here), and the code can be found on GitHub.

(Presenter tip: If you use a retina MacBook Pro or a new 2012 MacBook Air to present, remember to pack a MagSafe to MagSafe 2 converter so that you can charge your laptop using the power adapter that’s hopefully installed on the podium.)

MPFlipViewController: a page-flipping container controller


I put a project called MPFlipViewController up on GitHub. It’s a page-flipping container view controller that allows the user to flip through a series of view controllers as if they were pages in a book. It is based on the MPFlipTransition class I already have on GitHub, but instead of being just a transition between 2 views, it is a full-fledged container view controller that supports panning and swiping between pages (child view controllers). It follows the Containment API introduced in iOS 5, so it behaves as a proper container view controller similar to the system container controllers (e.g. UINavigationController, UITabBarController and UIPageViewController).

Requirements

Xcode 4.3
iOS 5
ARC

API

The API is based on the API for UIPageViewController (since they fulfill an almost identical role). There is a data source protocol that you implement to specify the previous and next pages, and a delegate protocol that you implement in order to receive feedback on whether or not a page-turn operation completed and also to optionally specify the new orientation in the event that device orientation changes (i.e. user rotates the device).

Use

To create a flip controller use the initWithOrientation: method and pass in the desired orientation (horizontal or vertical):

- (id)initWithOrientation:(MPFlipViewControllerOrientation)orientation;


To set the content use the setViewController:direction:animated:completion: method where direction indicates whether the animation should be a page flip forward or backward.

- (void)setViewController:(UIViewController *)viewController 
                direction:(MPFlipViewControllerDirection)direction 
                 animated:(BOOL)animated 
               completion:(void (^)(BOOL finished))completion;

To enable touch gestures (panning and swiping between pages) implement the MPFlipViewControllerDataSource delegate to provide the previous and next pages (if any). Return nil for either method to indicate the user is already on the first or last page.

- (UIViewController *)flipViewController:
          (MPFlipViewController *)flipViewController
      viewControllerBeforeViewController:
          (UIViewController *)viewController;

- (UIViewController *)flipViewController:
          (MPFlipViewController *)flipViewController
       viewControllerAfterViewController:
          (UIViewController *)viewController;

To be notified of whether a page turn animation completed or not, set the MPFlipViewControllerDelegate and implement the optional flipViewController:didFinishAnimating:previousViewController:transitionCompleted: method. This method is only called if the page turn was gesture-driven (i.e. in response to a pan or swipe), and not programmatic (i.e. in response to a call to setViewController:direction:animated:completion:).

- (void)flipViewController:(MPFlipViewController *)flipViewController 
        didFinishAnimating:(BOOL)finished 
    previousViewController:(UIViewController *)previousViewController 
       transitionCompleted:(BOOL)completed;

To change the orientation of the flip controller when device orientation changes, set the MPFlipViewControllerDelegate and implement the optional flipViewController:orientationForInterfaceOrientation: method and return the desired orientation.

- (MPFlipViewControllerOrientation)flipViewController:
                       (MPFlipViewController *)flipViewController 
                   orientationForInterfaceOrientation:
                       (UIInterfaceOrientation)orientation;

Demo Project

The GitHub project includes a sample project that demonstrates the use of the control and its API.

Don’t rename your .xcdatamodeld file

Ok, you can rename it but be prepared for weird bugs with Xcode 4.3 (and possibly earlier versions).  In my case I created a new Core Data model file and added it to an existing project.  Then I renamed the model file before attempting to build the project.  Yet even with a clean build and install (deleting the app from the device first), NSManagedObjectModel -initWithContentsOfURL returned nil even though my NSURL pointer was non-nil.

NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"ModelName"
    withExtension:@"momd"];
__managedObjectModel = [[NSManagedObjectModel alloc] 
    initWithContentsOfURL:modelURL];

Short answer: quit and reopen Xcode to fix the problem.

(Solution via Stack Overflow)

On the importance of setting contentsScale in CATextLayer

The retina display iPhone has been out for 2 years now, so I probably should have run into this before, but as I use CATextLayer infrequently I had not.  Anyway, if you don’t manually set the contentsScale property of your CATextLayer to the correct value, on retina devices you’ll end up with blocky pixel-doubled text like this:

However, if you call:

if ([layer respondsToSelector:@selector(setContentsScale:)])
    [layer setContentsScale:[[UIScreen mainScreen] scale]];

then you will get properly rendered text like so:


(Solution via StackOverflow.)

iOSDevCampDC 2012

I am pleased to announce that I will be speaking at iOSDevCampDC in Reston, VA on August 11.  This is a single track, single day mini-conference (for a mini price, just $50) that brings together a lot of DC-area developers.  It’s on a Saturday, so work conflicts are minimal.  Ticket sales end this Saturday (July 14), so if you’d like to go, act soon.

Update: registration has been extended until July 24.

I will be doing my “Enter The Matrix” presentation on matrix transformations, but I’ll probably fine-tune it to the more advanced audience I expect to quickly review the basics and then spend more time examining flipping and folding animations (and related ephemera such as anti-aliasing, avoiding off-screen render passes, shadows, rendering retina images, etc.)

So if you’re in the area (or will be), I hope to see you there!  (Seriously, for $50 you can’t beat the price for 6 technical sessions plus the opportunity to network with a cadre of local devs.)

CocoaConf DC wrap-up

Last weekend I had a great time at CocoaConf DC on my home turf of northern Virginia.  In many ways CocoaConf is the antithesis of WWDC.  Capped at 100 attendees, it’s small where WWDC is enormous, and intimate where WWDC can be impersonal.  It’s a conference where you can meet (and spend time chatting with) every speaker and every attendee, if you put your mind to it.  And really, although the sessions are the ostensible reason to attend conferences (the way we justify them to our bosses or to ourselves), for me at least it’s the personal connections you make that are the real value.  As developers we tend to spend most of our time isolated in our cubicles (and in some cases our homes), so it’s doubly important to take the time to reach out to the like-minded individuals we encounter at conferences.  CocoaConf is a great venue for that.  And with 3 tracks of 30 sessions over 2 days (preceded by an optional full-day intensive tutorial), there’s plenty of knowledge to be gained as well.  A common theme I heard throughout last weekend was the “which of these sessions should I attend next?  They all sound great” dilemma (trilemma?).

This time I presented two talks.  I debuted a talk on custom container view controllers (using the iOS 5 containment API), which in addition to containment covered the iterative process of designing and refining the UI and API for a page-flipping control (demo app shown above).  The slides are available here and the code (open source and attribution-only licensed) is up on GitHub.

My other talk, “Enter The Matrix”, I gave for the second time.  It’s about using matrix transformations in Quartz drawing, UIKit animations, and CoreAnimation (with obligatory, gratuitous references to the Matrix movies).  Since Chicago in March, I’ve significantly improved the accompanying sample app, completely revised the slides for fold and flip animations, and introduced a section on skew and perspective.  The slides are available here (latest version here) and the code is on GitHub.

I think one new talk is the most I can manage per conference.  A great deal of work goes into each presentation (code, slides, and speech).  I like this picture of Saul Mora, Scott McAlister and me all preparing for our talks on the first day of the conference.  We work on our presentations up until the last minute not because we’re ill-prepared, but rather because we care.

One little touch that I really appreciated as a speaker: a MacBook power adapter installed on the presenter’s table in each of the rooms.  Sometimes, it’s the attention to little details that really stand out.

I hear that CocoaConf may be back in DC next March.  If so, I hope I can be a part of it again, either as speaker or attendee.