WWDC 2012 and indie conferences

Of course the big news yesterday was Apple’s announcement of WWDC 2012 and then it’s subsequent record sellout in a little under 2 hours.  I was fortuitous enough to have the means and opportunity to secure a ticket for myself, but many developers were not.

Fortunately for those wishing to participate in the technical instruction and peer networking of an iOS/Mac tech conference, there are a number of excellent indie conferences.  These conferences have the benefits of being both more intimate (i.e. easier to network) and more affordable than WWDC while still having great technical content.  Two excellent American conferences are CocoaConf (at which I will be speaking in June), a roaming conference that occurs in various underserved (in the technical conference sense) cities, and 360iDev which has now settled in beautiful Denver, CO every September.  Scott McAlister has compiled a list of 2012 iOS tech conferences on his blog here.

So if you crave that conference fix but didn’t get a ticket to WWDC this year (or even if you did), don’t offer to legally change your name, just go attend one of the many outstanding indie conferences instead.

CocoaConf DC 2012

I am extremely pleased to announce that I will be speaking at CocoaConf DC (actually in Herndon, VA) at the end of June.  This time I will be giving two presentations: one on container view controllers and the other on matrix transformations.

There are a slew of expert speakers on the schedule, and I’m really looking forward to attending as well as speaking.  At CocoaConf Chicago last month I met a bunch of great developers, watched some excellent presentations, learned many things, and just generally renewed my enthusiasm for iOS development.  And the conference hotel is just a few miles from the Air & Space Museum where Discovery now resides.  So book now while early bird tickets at $200 off are still available!

Title: Implementing Custom Container View Controllers

Abstract: iOS 5 introduced the ability to create your own custom container view controllers. Prior to iOS 5 you had to use only the stock controllers (tab, navigation, splitview, etc.) or attempt to roll your own, which was a complex endeavor and often hacky. Custom container view controllers are a great way to give your app a unique look and feel. Learn how to implement your own custom container view controller using the new API. We’ll build a page-flipping controller and cover the various gotchas that can arise along the way. The final product will be an open-source controller that you are free to use in your own apps or just study and take apart.

Title: Enter The Matrix

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.  Tons of demos and full open-source source code provided.

On the importance of setting shadowPath

It’s super easy to add drop shadows to any view in iOS. All you need to do is

  1. add QuartzCore framework to your project (if not there already)
  2. import QuartzCore into your implementation file
  3. add a line such as [myView.layer setShadowOpacity:0.5]

and voilà, your view now has a drop shadow.

However, the easy way is rarely the best way in terms of performance.  If you have to animate this view (and especially if it’s part of a UITableViewCell) you will probably notice stutters in the animation.  This is because calculating the drop shadow for your view requires Core Animation to do an offscreen rendering pass to determine the exact shape of your view in order to figure out how to render its drop shadow.  (Remember, your view could be any complex shape, possibly even with holes in it.)

To convince yourself of this, turn on the Color Offscreen-Rendered option in the Simulator’s Debug menu.

Alternately, target a physical device, launch Instruments (⌘I), choose the Core Animation template, select the Core Animation instrument, and check the Color Offscreen-Rendered Yellow option.

Then in the Simulator (or on your device) you will see something like this:

Which indicates that something (in our case the drop shadow) is forcing an expensive offscreen rendering pass.

The quick fix

Fortunately, fixing the drop shadow performance is typically almost as easy as adding a drop shadow.  All you need to do is provide Core Animation with some information about the shape of your view to help it along.  Calling setShadowPath: on your view’s layer does exactly that:

[myView.layer setShadowPath:[[UIBezierPath 
    bezierPathWithRect:myView.bounds] CGPath]];

(Note: your code will vary depending on the actual shape of your view.  UIBezierPath has many convenience methods, including bezierPathWithRoundedRect:cornerRadius: in case you’ve rounded the corners of your view.)

Now run it again and confirm that the yellow wash for offscreen-rendered content is gone.

The catch

You will need to update the layer’s shadowPath each time the bounds of your view change.  And if you’re animating a change to bounds, then you will also need to animate the change to the layer’s shadowPath to match.  This will need to be a CAAnimation because UIView cannot animate shadowPath (which is a property on CALayer).  Fortunately, it is straight-forward to animate from one CGPath to another (from the old to new shadowPath) via CAKeyframeAnimation.

On the importance of setting contentScaleFactor in CATiledLayer-backed views

If you look at any samples for CATiledLayer (such as ZoomingPDFViewer), you will invariably see code like this:

// to handle the interaction between CATiledLayer and high // resolution screens, we need to manually set the tiling view's // contentScaleFactor to 1.0. (If we omitted this, it would be 2.0 // on high resolution screens, which would cause the CATiledLayer // to ask us for tiles of the wrong scales.)
pdfView.contentScaleFactor = 1.0

Without this line, on retina devices such as iPhone 4/4S or the latest iPad, your view will probably ask for 4x as many tiles as are necessary.  (Note that retina screens have 4x as many pixels as their non-retina counterparts, so unless you are doubling the dimensions of CATiledLayer tileSize for retina, your view will already ask for 4x as many tiles as for non-retina, so in this case your retina view would ask for 16x as many tiles as its non-retina version.)

That’s all well and good.  The point I wanted to make in this post was simply a reminder that if you are doing any view juggling with your CATiledLayer-backed views (such as dequeueing them for reuse as pages in an infinite scroll view), you need to set contentScaleFactor each time you add your view to the view hierarchy.  Otherwise, it will take on the contentScaleFactor of its new superview and it will start asking for the wrong tile sizes.

Update: It’s not just view juggling. Any time your CATiledLayer-backed view or any view higher up in its view hierarchy is added to a parent view hierarchy, then contentScaleFactor of your view will reset to 2 on a retina device.  This includes switching tabs in a UITabBarController or pushing a new view controller onto the navigation stack of a UINavigationController.  The way I’ve handled this is by overriding viewWillAppear: in the UIViewController that contains the CATiledLayer-backed view.

Update 2: Aaron Farnham wrote me to suggest that you simply override didMoveToWindow in your CATiledLayer-backed UIView and call setContentScaleFactor:1 there.  Like so:

- (void)didMoveToWindow {
    self.contentScaleFactor = 1.0;