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.

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.)