Sorry

Well, I got back from WWDC and there was just too much to do, so I’ve been neglecting my blog a little bit. But since I already missed one post on AltDevBlogADay and today I was about to miss another (3 strikes and I’m out???) I decided to get something quick but maybe useful for all you iOS devs out there.

I’ll share two tricks I recently had to use for Snap. One I learned during one of the labs at WWDC and it’s an old but very hidden trick that’s not covered by NDA so I can share. The other is something that I hacked on my own but got somewhat validated by an Apple engineer I showed to, so not I feel more confident in showing this in public…

First trick

Snap is a camera app and my users were asking me to implement zooming. I studied the API a bit and there was no way to tell the camera to zoom. What I came up with (and Apple’s engineers that work with this API have said it’s the right thing to do) was to change the frame of the camera preview layer so that it would “bleed” out of the view and then give the illusion of zoom. After taking the picture I have to crop but that’s another story.

My problem was that when I changed the frame of the layer, even though I was not applying any animation, the system would animate my change and the interface felt a little weird. It felt like the zoom was “bouncing”. It’s hard to explain but the result was not good and I could not figure out how to remove this animation.

During one the the labs I asked an Apple engineer about this and as he was about to go looking for the answer another attendee from across the table who overheard me said he knew how to do this and very quickly guided us to the documentation where this little trick is hidden.

So, inside the “Temporarily Disabling Layer Actions“:

[CATransaction begin];
 
  [CATransaction setValue:(id)kCFBooleanTrue
 
                   forKey:kCATransactionDisableActions];
 
  // Do what you want with your layer
 
  [CATransaction commit];

So there you have it. Very obscure but it works. You can also change the duration if this is what you want:

[CATransaction begin];
 
  [CATransaction setValue:[NSNumber numberWithFloat:10.0f]
 
                   forKey:kCATransactionAnimationDuration];
 
  // Do whatever you want with your layer
 
  [CATransaction commit];

Second trick

Another problem I faced with Snap was that, even though the saving of the image takes place mostly on the background using block and GCD (more about this on another post…), in order to compose the image I still had to make the user wait. I can do it on the background but that would involve copying a lot of memory and I didn’t want to do this on the iPhone. And it’s fast enough not to be a problem but I didn’t like that the interface froze when I was composing the image with the notes and the user was just staring at an unresponsive device.

So, I decided to use MBProgressHUD to at least show something to the user. My problem was that I had a lot of calls to the method that generates the image and the caller expects to get the UIImage back. As the calls are made on the main run loop and the method takes too long the interface froze and the HUD would not show.

Yes, I could have refactored everything to use GCD and callback blocks but I had to release an update and didn’t have much time. So, I decided to pump the main queue myself:

    // Will return my image here
 
      __block UIImage *img = nil;
 
      // To indicate the work has finished
 
      __block BOOL finished = NO;
 
   
 
      // This will execute on another thread. High priority so it's fast!
 
      dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
 
          // Call my very long method and indicated we're finished
 
          img = [[self annotatedImage] retain];
 
          finished = YES;
 
      });
 
   
 
      // This will probably execute even before my image method above
 
      MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:view animated:YES];
 
      hud.labelText = label;
 
   
 
      // Get the main run loop
 
      NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
 
      // Run until finished
 
      while (!finished) {
 
          [runLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.01f]];
 
      }
 
      // Hide the HUD
 
      [MBProgressHUD hideHUDForView:view animated:YES];
 
   
 
      // Return my image that is now composed.
 
      return [img autorelease];

Even though it’s kind of an ugly hack it can be used in situations where you’ll really have to make the user wait and a synchronous call on the main thread is what you already have or what it’s faster for you to implement.

I don’t recommend this for every situation. There are situations where this might lead to a deadlock in your app, so test this a lot if you decide to use it. It worked for me. And, as I said, I showed this to an Apple engineer during one of the labs and he said it was a good solution to the problem.

I have a fork of MBProgressHUD and have used these principles to build a category for MBProgressHUD that does this AND can even be cancelled by the user. This version is even hackier so I won’t go into it right now for lack of time but if someone wants to read about it just ask in the comments and I’ll do it.

An afterthought about WWDC

One of the things I learned during last year’s WWDC is that, even though the sessions are great, the labs are even better. And as the sessions are not opened for questions and they’re usually out on iTunes for you to watch less than 2 weeks after the event this year my main priority were the labs. I went to every lab that I could think of and even went to some twice.

So, my advice to any WWDC attendees: Forget the sessions and go to the labs! The sessions you can watch later at home but you only have access to these great engineers that are building the stuff we use for these 5 days, so make the most of this. Even if you have a stupid question, don’t be shy and go to a lab and ask the question. These guys are great and are always willing to help. This is consulting from Apple that is well worth the US$1600 bucks. I dare to even say that 1600 is cheap! (don’t tell Apple though…)

I even bumped into a guy that helped me  last year that remembered me, my problem and tried to help me again this year even though my problem now was not at all related to his expertise. Nice guy. Thanks again Sam. See you next year.

Oh, and have I mentioned that you should get Snap!

I’ll be writing every 15 days here but I try to post at least once a week on visit my blog and subscribe.

Well, that’s it. Sorry for the quick post. I’ll come up with something better next time. And if you have any comments on this post please leave them here and I’ll try to respond and correct whatever you guys come up with.