Device orientation vs interface orientation

Just today I got bit by confusing device orientation and interface orientation. I really should know better. Device orientation is of course the orientation that the device is currently being held in, while interface orientation is the orientation of the running app’s user interface.

What I was trying to do was to hide the status bar while in landscape mode and show it in portrait mode for an iPhone app that operates in the 3 principal orientations: portrait, landscape left, and landscape right.

To achieve that I was using code like this:

#pragma mark - Status Bar

- (BOOL)prefersStatusBarHidden
{
    return (UIDeviceOrientationIsLandscape([[UIDevice currentDevice] orientation]));
}

#pragma mark - Orientation

- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
    [super willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration];
    
    [self setNeedsStatusBarAppearanceUpdate];
}

This works fine at first blush, especially in the simulator, but not so well in practice. First off, the app doesn’t even support all 4 possible interface orientations (the fourth being upside down portrait), so what happens when the phone is held upside down? Well the interface orientation doesn’t change from its previous orientation (most likely landscape) but the device orientation is not landscape and so the status bar appears. Bug.

But even worse, there are additional device orientations (namely face up and face down) that are neither portrait nor landscape and have no matching interface orientation. If the phone was last in landscape interface orientation and then gets laid flat on the desktop, the device orientation is no longer landscape (it is flat), and so the status bar appears. Bug again.

Just for the record, here’s the definition of UIDeviceOrientation:

typedef NS_ENUM(NSInteger, UIDeviceOrientation) {
    UIDeviceOrientationUnknown,
    UIDeviceOrientationPortrait,            // Device oriented vertically, home button on the bottom
    UIDeviceOrientationPortraitUpsideDown,  // Device oriented vertically, home button on the top
    UIDeviceOrientationLandscapeLeft,       // Device oriented horizontally, home button on the right
    UIDeviceOrientationLandscapeRight,      // Device oriented horizontally, home button on the left
    UIDeviceOrientationFaceUp,              // Device oriented flat, face up
    UIDeviceOrientationFaceDown             // Device oriented flat, face down
};

And here’s the definition for UIInterfaceOrientation:

typedef NS_ENUM(NSInteger, UIInterfaceOrientation) {
    UIInterfaceOrientationPortrait           = UIDeviceOrientationPortrait,
    UIInterfaceOrientationPortraitUpsideDown = UIDeviceOrientationPortraitUpsideDown,
    UIInterfaceOrientationLandscapeLeft      = UIDeviceOrientationLandscapeRight,
    UIInterfaceOrientationLandscapeRight     = UIDeviceOrientationLandscapeLeft
};

It’s interesting that UIInterfaceOrientation is defined in terms of UIDeviceOrientation.

TL;DR;

So what I should have been doing was this:

#pragma mark - Status Bar

- (BOOL)prefersStatusBarHidden
{
    return (UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation]));
}

This works as expected when the device is upside down or flat.

CocoaConf San Jose wrap-up

This past weekend I had the pleasure of attending and speaking at CocoaConf San Jose. This was the 6th CocoaConf event I’ve attended, and I believe it was one of the best. Highlights for me included:

  • Matt Drance‘s opening keynote where he discussed the importance of people in our work: our customers (users), our co-workers, and ourselves. This is something that we as engineers can often lose sight of.
  • Jaimee Newberry‘s session on brainstorming. Jaimee is a fantastic speaker and always fun to watch, and there was plenty of info on how to manage and get the most out of brainstorming sessions, which I’d like to try on my next project.
  • Ben Lachman‘s session on prototyping, which covered a variety of tools and workflows and included a demo of the yet to be released Briefs 2 (which I plan on buying as soon as it is available. Seriously, just take my money already!).
  • Marcus Zarra‘s session on the MVC-N design pattern, which contained a strong admonition against relying on 3rd party code (especially networking code). This is going to change how I approach the next client project I manage. This talk alone made attending the conference worthwhile.
  • Daniel Pasco‘s session on the long road and various pitfalls encountered in transforming his company, Black Pixel, from a client-work company into a product company (Black Pixel makes the excellent Kaleidoscope 2 — you should buy it). Valuable lessons for anyone seeking to go indie.

I presented 2 talks of my own. Thursday evening I delivered a talk on UICollectionView. This was the 4th (and probably final) time I delivered this talk over the past 6 months. The sample app (which contains 5 different layouts and multiple examples of advanced customizations) is available on GitHub here, while the slides can be downloaded here.

photo pinch

Saturday morning I presented “Animation: From 0 to Awesome in 90 Minutes”, which is an animation talk that begins with some design principles of animation (drawn from Phil Letourneau’s portion of our joint animation session at Renaissance), proceeds to discuss UIKit and Core Animation (and the limitations of UIKit), then takes a close-up look at flipping and folding animations, and wraps up with some general graphical performance tips. This talk is sort of an evolution of both the Renaissance talk and my Enter The Matrix: Reloaded sessions from 2012, and yet is also its own thing. I debuted it last month at CocoaConf DC and this was its second iteration. The sample app is available on GitHub here, while the slides are available for download here. My favorite portion of the app is the touch-enabled timing curve widget that lets you create custom cubic bezier curves by dragging 2 control points (and shows you their values). I think that was also one of the big takeaways from the talk: that you can have an animation overshoot its endpoint and then snap back by simply applying the right timing curve to a basic animation with no need for multiple or keyframe animations.

Timing curve

This wraps up the CocoaConf Spring Tour and my own 3 conference “Spring Tour” as well. I plan to take the summer off from speaking and pick it up again in the Fall.

A potential pitfall of CGRectIntegral

This morning while I was checking an app for misaligned elements, I happened upon a misaligned button. (If you’re not using either the iOS Simulator or Instruments to check your app for misaligned images, you should be, but that’s a post for another day.)

Checking the code it was obvious to me where the problem was.

backButton.frame = CGRectMake(5, (navigationBar.bounds.size.height
    - imageBack.size.height)/2, imageBack.size.width,
    imageBack.size.height);

Centering code is especially prone to pixel misalignment. In this case imageBack has a size of (50, 29) while the navigationBar has a height of 44 points. The code above generates a rect with origin = (5, 7.5) and size = (50, 29). So the image ends up vertically misaligned, which in turn makes the child text label inside also misaligned, and hence they show up painted in magenta when the Color Misaligned Images option is checked in the iOS Simulator Debug menu.

This looks like a job for CGRectIntegral, right? But when I change the code to this:

backButton.frame  = CGRectIntegral(CGRectMake(5, 
    (navigationBar.bounds.size.height - imageBack.size.height)/2,
    imageBack.size.width, imageBack.size.height));

I end up with this:

The button is no longer misaligned, but it is now being stretched (hence the yellow wash). Debugging shows that CGRectIntegral has converted the input rect of (5, 7.5) x (50, 29) into (5, 7) x (50, 30). So now the image is being stretched vertically by 1 point. That might be fine for UILabel but not for an image.

The other issue with using CGRectIntegral is that the original rect is actually fine for retina devices because they have 2 pixels per point, so a value of 7.5 actually falls on a pixel boundary, and is the optimal centering for this image. If we adjusted it to origin.y = 7 (without stretching) then it would be 2 pixels closer to the top than to the bottom on a retina device.

I’ve written some helper functions to correctly pixel align rectangles (not point align) for both retina and non-retina screens, and posted them in this gist.

Under non-retina it would convert the rectangle to (5, 7) x (50,29) to pixel align it without stretching, while under retina it would leave the rectangle unmodified at (5, 7.5) x (50, 29).

This finally clears the magenta (alignment) and yellow (stretch) washes from the button:

Addendum

According to the Apple Documentation for CGRectIntegral:

A rectangle with the smallest integer values for its origin and size that contains the source rectangle. That is, given a rectangle with fractional origin or size values, CGRectIntegral rounds the rectangle’s origin downward and its size upward to the nearest whole integers, such that the result contains the original rectangle.

The fractional origin of (5, 7.5) is rounded downward to (5, 7), but I initially thought the size would be left unmodified (not rounded up) because it already comprises 2 whole integers. But that wouldn’t contain the original rectangle, whose lower right corner is positioned at (55, 36.5). In order to contain the original rectangle, the height has to be increased by 1 point from 29 to 30.

How to Add a Decoration View to a UICollectionView

Decoration views are one of the great new features of collection views.  Unlike cells and supplementary views (e.g. headers and footers), decoration views are layout-driven rather than data-driven.  You can think of them as the shelves in the iBooks Store interface.  Background views are another good candidate for implementing as decoration views.

So how exactly do you add decoration views to your collection view?  It’s actually pretty simple.

  1. Create your decoration view.  It should be a subclass of UICollectionReusableView.
  2. Create your own UICollectionViewLayout subclass if you don’t already have one.  This can be a subclass of UICollectionViewFlowLayout if that’s what you’re using.
  3. Register your decoration view with your layout either by class name or nib.
  4. Return the appropriate attributes for your decoration view in layoutAttributesForElementsInRect:
  5. Implement layoutAttributesForDecorationViewOfKind:atIndexPath: to return attributes for the specified decoration view.


In my sample project IntroducingCollectionViews, the first layout, GridLayout, is an iBooks Store-style layout derived from UICollectionViewFlowLayout.  The layout has shelves underneath each row of cells that are implemented as decoration views.  I will go through the steps above to show how I implemented the shelf views in the GridLayout class.

Create the decoration view

I created a ShelfView class derived from UICollectionReusableView.  It’s a very simple view (no subviews) — I just set backgroundColor to a color from a pattern image and add a drop shadow.  (Performance tip: I set the shadowPath for the drop shadow on the backing layer.  See here for why that’s important.)

Subclass UICollectionViewLayout

I created GridLayout as a subclass of UICollectionViewFlowLayout.  The superclass handles the heavy lifting of positioning all the cells and headers and footers, but I have to handle the decoration views.

Register the decoration view

In the init method for GridLayout I register the shelf view by class.

[self registerClass:[ShelfView class] forDecorationViewOfKind:
    [ShelfView kind]]

(Where kind is a convenience class method I added to return the kind string. Kind becomes important when you have multiple decoration view classes in your collection view.)

Implement layoutAttributesForElementsInRect:

This method is the meat of a layout.  It’s where the layout tells the collection view about all items (cells, supplementary views, and decoration views) that are to appear within a given rect (generally the current visible rect based on the collection view’s current contentOffset).  The collection view in turn takes over instantiating (or reusing) all necessary views and setting their position, size, and other visible characteristics (alpha, transform, zIndex, etc.).

Because I’m deriving from UICollectionViewFlowLayout, I can just call super to get the attributes for all the cells and supplementary views.  I just need to add the decoration views (if any) to that array.  But in order to know which decoration views might fall within a given rect, I need to do some calculations ahead of time.  To do that I need to override prepareLayout.  This is the method that gets called every time the layout becomes invalidated and is where you can calculate where all the collection elements should go.

In prepareLayout I calculate where the shelves will go (don’t forget to call super).  Unfortunately, this means replicating much of the flow layout positioning since I want to position each shelf directly under each row (line) of speakers (actually each shelf goes under the speaker name label of each SpeakerCell).  I calculate the frame of every shelf rect in the entire content area and store them in a dictionary keyed on index path where section is of course the section the shelf resides in and row is the 0-based index of the shelf within each section moving top to bottom. (This will be important when it’s time to implement layoutAttributesForDecorationViewOfKind:atIndexPath:)

Meanwhile, back in layoutAttributesForElementsInRect: I of course call super and then I enumerate through all my shelf rects and for any shelf rect that intersects with the specified rect (meaning a decoration view should appear at that position), I create a set of layout attributes for that decoration view and add it to the array of attributes returned by super.  To create the attributes I call

[UICollectionViewLayoutAttributes 
     layoutAttributesForDecorationViewOfKind:[ShelfView kind] 
                               withIndexPath:key]

Where I just pass in the kind and the index path (which happens to be the key of the dictionary I’m enumerating through).  Then I set the frame to be the rect I have stored and also set zIndex to 0 (I set zIndex to be 1 on all the cells and supplementary views to make sure they appear in front of the shelves).

Implement layoutAttributesForDecorationViewOfKind:atIndexPath:

This one is pretty easy.  Because I stored my shelf rects in a dictionary keyed on index path, I just use the passed index path to fetch the shelf rect and then I just create the attributes exactly as I did for layoutAttributesForElementsInRect:  (Actually I have yet to see this particular method ever get called.  I have a breakpoint set on it and have never had it be triggered.)

Summary

And that’s what it takes to add decoration views to a collection view.  Decoration views are a cool new feature of collection views and are just one of the improvements of UICollectionView over its predecessor for data display on iOS, UITableView.

Once again, the sample project is on GitHub here. The wood paneling backgrounds are part of a set of retina-ready repeatable assets by Glyphish.

Go forth and make amazing collection view layouts!

Want $100 off early bird registration for Renaissance?

Then I’ve got a discount code for you: use “mpospese” (my Twitter and ADN handle), or just use this direct link.  The code is good through the end of November and gets you into Renaissance for just $500.

Did I mention that there’s going to be a live concert by The Smule Band?  These are the guys behind Ocarina, Leaf Trombone, Magic Piano, and several other apps that turn iOS devices into musical instruments.  Ocarina holds a special place in my heart because using it for the first time was sort of a watershed moment for me in my career as a developer.  I had already been programming for mobile devices for 9 years at that point, but had never seen anything as creative as Ocarina – a device that transformed a phone into a multi-touch wind instrument (you blow past or into the microphone)!  I was already tinkering with the iPhone SDK at that point, but that was when I knew that I really, really wanted to focus on the iPhone platform.  Anyway, as you can tell I’m super excited to see them perform and hope to be able to chat with their team afterward.

The rest of the speaker lineup (myself included) is incredibly diverse and covers pretty much all the aspects of creating apps and running a business of creating apps in today’s market.  It’s a great time to be a developer, designer, or businessperson (or all three), and I plan on learning everything I can that week, especially in the areas that typically fall outside of my comfort zone.

Renaissance

The Art And Science Of Apps


Tim Burks and Bill Dudney are collaborating to put on a new conference for App Makers early next year in San Francisco: Renaissance.  The conference aims to be (roughly) equal parts design, business, and technology.  This is a 3-day single track conference, so you don’t have to miss anything.  The entire speaker roster has yet to be announced, but already includes folks like Daniel Pasco, Rob Rhyne, Brent Simmons, James Dempsey, Chris Clark, and Matt Drance.

I am proud to announce that I will be presenting a session on animation (the technical half) in conjunction with Phil Letourneau of Black Pixel (the design half).  I’m really excited about the session format (50/50 design/technology).

Early bird tickets are available through the end of November.  Even better, for $200 more you can get a combo Renaissance / CocoaConf ticket that is good for Renaissance plus any 2013 CocoaConf conference.  There are four confirmed dates/cities for next Spring (Chicago, DC, San Jose, and Dallas) with more planned for the Fall.

Personally I think this conference is going to be epic.  If you’re an indie, startup, or small company iOS App Maker, you’ll especially want to be there.  The main sessions look great and cover a wide variety of topics, and I’m certain all the informal breakout sessions and after hours conversations will be fantastic.

Update: Use code “mpospese” to register by November 30 and receive an extra $100 off the early bird price!

CocoaConf PDX wrap-up / Raleigh preview

Photo courtesy of Gordon Hughes

Last weekend I attended CocoaConf PDX in Portland, OR.  Dave Klein really upped his game with keynotes from Daniel Pasco and Brent Simmons, and speakers such as James Dempsey and Collin Donnell.  The event was sold out (the second one to do so I believe, the other being Chicago), so there was a good crowd of about 100 developers on hand.  As always it’s a great vibe with so many people eager to learn and share.

Recap

I presented talks on matrix transformations and collection views.  This was the 4th time I’ve given my matrix transformation talk, “Enter The Matrix: Reloaded“, and I feel like it’s really starting to hit its stride.  I revamped it in August to include a lot more information about flipping and folding animations as well as some general graphics performance tips.  Apparently it was voted the 2nd favorite session of the conference, losing out by a single vote to Jonathan Penn‘s UIAutomation talk.  I consider that a great compliment because Jonathan is a fantastic speaker and his automation talk is really a lot of fun (no, really!) and gets the crowd cheering by the end.  The slides are available here and the code is on GitHub.

My second talk, “Introducing Collection Views”, was a new talk.  I sought to cover the basics of collection views, layouts, and attributes while also covering the more advanced topics of custom layouts and animations and providing specific tips borne from personal (sometimes painful) experience.  The talk is accompanied by a sample app that displays a single collection view with five different layouts.  The slides are here and the code is on GitHub.

Next

I will be presenting at CocoaConf RTP in Raleigh, NC at the end of this month.  Tickets are still available and it looks to be another great conference.  Bill Dudney will be there (including an all-day graphics tutorial) – need I say more?

I will be presenting the same two talks for Raleigh.  I’m looking forward to the additional polish and reworking I can bring to the collection views talk for its second rendition.


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.


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 hope to see you in Raleigh at the end of the month – it’s going to be a good time!

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.