
This isn’t a recipe so I’ll keep it short: I’m thrilled to announce that I’m joining Zaarly to head up their mobile development.
You can read more on my personal blog: Zaarly « Peter Boctor.

This isn’t a recipe so I’ll keep it short: I’m thrilled to announce that I’m joining Zaarly to head up their mobile development.
You can read more on my personal blog: Zaarly « Peter Boctor.
Full Source code: https://gist.github.com/956403
You spend a lot of time and effort building your app, writing countless view controllers. You think it’s near perfect.
Then one of your beta testers (or customers, or app reviewers) finds a problem. You look into it and realize that it only happens after a low memory pressure warning.
You should have written your viewDidLoad to handle getting called after a low memory pressure warning, but you didn’t.
Wouldn’t it be great if you were forced to write your viewDidLoad implementation with low memory situations in mind? After all, out in the wild on real world devices, it’s very likely that sooner or later every single one of your view controllers will have to handle this.
Add this code to your project and have your view controller inherit from BaseViewController instead of UIViewController. Every time viewWillAppear is called, the simulator will force a low memory warning:
- (void)simulateMemoryWarning
{
#if TARGET_IPHONE_SIMULATOR
#ifdef DEBUG
CFNotificationCenterPostNotification(CFNotificationCenterGetDarwinNotifyCenter(), (CFStringRef)@"UISimulatedMemoryWarningNotification", NULL, NULL, true);
#endif
#endif
}
I found myself in this pattern one too many times. I finally decided to find a solution.
The iOS Simulator has the menu Hardware -> Simulate Memory Warning
But I wasn’t going to select that menu item every time you test to see if something works.
I embarked on figuring out how this menu item works.
I looked through the UIKit framework binary (/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator4.3.sdk/System/Library/Frameworks/UIKit.framework/UIKit) searching for words like memory and warning.
I put breakpoints in gdb trying to find what message is sent. I read the NSNotification docs. After many dead ends I discovered the name of the notification: UISimulatedMemoryWarningNotification and that I needed to use CFNotificationCenter to send the message.
I’ve tried this out on a couple of projects and it has forced me to think about low memory situations from day one.
Full Source code: https://gist.github.com/956403
Full Source code: https://github.com/boctor/idev-recipes/tree/master/SideSwipeTableView
The Twitter iPhone app pioneered the ability to swipe on a tweet and have a menu appear, letting you do things like reply or favorite the tweet.

Tweets in the Twitter app are table view cells in a table view. How do we recreate this feature and add the ability to side swipe on table view cells?
This feature has two distinct parts. The first is detecting that the user swiped on the table. The second is animating in and animating out the menu view.
iOS 4 introduced Gesture Recognizers which make gestures like swiping, tapping and pinching very east to detect. Specifically we can create a couple of UISwipeGestureRecognizer objects, one for the right direction and another for the left detection and attach them to the table view:
// Setup a right swipe gesture recognizer UISwipeGestureRecognizer* rightSwipeGestureRecognizer = [[[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeRight:)] autorelease]; rightSwipeGestureRecognizer.direction = UISwipeGestureRecognizerDirectionRight; [tableView addGestureRecognizer:rightSwipeGestureRecognizer]; // Setup a left swipe gesture recognizer UISwipeGestureRecognizer* leftSwipeGestureRecognizer = [[[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeLeft:)] autorelease]; leftSwipeGestureRecognizer.direction = UISwipeGestureRecognizerDirectionLeft; [tableView addGestureRecognizer:leftSwipeGestureRecognizer];
Now when the user swipes left or right anywhere in the table, our swipeRight: or swipeLeft: methods will get called. The touch handling code that tracks the user’s finger and figures out their intent is blissfully handled for us.
When this feature was introduced in what was then the Tweetie app, it worked in iOS 3 well before iOS 4 was released. You might smartly argue that if iOS 3 currently makes up 1-2% of users out there it isn’t worth developing for and I admit this is a valid point. Still it’s an interesting technical mystery and that’s just the kind of thing we love solving!
You might have thought like I did, that the Twitter app must have implemented its own touch handling, guessing based on the location of your finger whether you were trying to do a swipe, but this is wrong.
In the Twitter app it doesn’t matter if you swipe left or swipe right, the animation of the menu always happens from left to right. This is the same behavior as the editing of table view cells and it turns out this is how the Twitter app does it: It hijacks the built in swipe to delete feature of table view cells. There are 3 parts to making this work:
To enable the swipe-to-delete feature of table views (wherein a user swipes horizontally across a row to display a Delete button), you must implement the tableView:commitEditingStyle:forRowAtIndexPath: method
So the first step is to implement the tableView:commitEditingStyle:forRowAtIndexPath: method.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
}
The method doesn’t have to do anything. Once it is implemented, you’ll be able to side swipe on a cell and the Delete button will appear.
Apple’s Inserting and Deleting Rows and Sections documentation indicates that when you explicitly put a table in editing mode by calling setEditing:animated:, the same message is then sent to each of the visible cells.
The documentation for a table view cell’s setEditing:animated: indicates that when this method is called, insertion/deletion control are animated in.
So disabling the Delete button turns out to be relatively simple: Override the table view cell’s setEditing:animated: and don’t call the superclass’s implementation.
- (void)setEditing:(BOOL)editing animated:(BOOL)animated
{
// We suppress the Delete button by explicitly not calling
// super's implementation
if (supressDeleteButton)
{
// Reset the editing state of the table back to NO
UITableView* tableView = [self getTableView:self];
tableView.editing = NO;
}
else
[super setEditing:editing animated:animated];
}
Apple’s docs for tableView:willBeginEditingRowAtIndexPath: are crystal clear: This method is called when the user swipes horizontally across a row.
The implementation of this method in iOS 3 is a parallel to the swipeLeft: and swipeRight: methods we registered with UISwipeGestureRecognizers under iOS 4. When any of these methods are called, we know that a swipe happened and we are ready to animate in the menu.
Before we animate in the menu, we first add it as a subview of the table view.
As you can see in the image at the top of this post, we are animating the existing cell content offscreen while simultaneously animating in the menu. Here is a rough illustration of how both the cell content and the menu have to animate in sync during a left to right animation:
So we first set the frame of the menu, placing it offscreen. Depending of the direction, we’d put it offscreen on the right or left side of the table. Next we’d start an animation block and set the frame of the menu to be at 0 x-offset. Inside the same animation block we also set the cell’s frame to be offscreen on the other side of the table.
- (void) addSwipeViewTo:(UITableViewCell*)cell direction:(UISwipeGestureRecognizerDirection)direction
{
// Change the frame of the side swipe view to match the cell
sideSwipeView.frame = cell.frame;
// Add the side swipe view to the table
[tableView addSubview:sideSwipeView];
// Remember which cell the side swipe view is displayed on and the swipe direction
self.sideSwipeCell = cell;
sideSwipeDirection = direction;
// Move the side swipe view offscreen either to the left or the right depending on the swipe direction
CGRect cellFrame = cell.frame;
sideSwipeView.frame = CGRectMake(direction == UISwipeGestureRecognizerDirectionRight ? -cellFrame.size.width : cellFrame.size.width, cellFrame.origin.y, cellFrame.size.width, cellFrame.size.height);
// Animate in the side swipe view
animatingSideSwipe = YES;
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.2];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:@selector(animationDidStopAddingSwipeView:finished:context:)];
// Move the side swipe view to offset 0
sideSwipeView.frame = CGRectMake(0, cellFrame.origin.y, cellFrame.size.width, cellFrame.size.height);
// While simultaneously moving the cell's frame offscreen
// The net effect is that the side swipe view is pushing the cell offscreen
cell.frame = CGRectMake(direction == UISwipeGestureRecognizerDirectionRight ? cellFrame.size.width : -cellFrame.size.width, cellFrame.origin.y, cellFrame.size.width, cellFrame.size.height);
[UIView commitAnimations];
}
When the menu is animated away, there is a little bounce of the cell content as it comes back into view.
Here is another rough illustration showing the three animations that make up a bounce, showing at each step where the cell content and menu are:

There are multiple ways we can achieve this animation. One is CAKeyframeAnimation where you specify a path and the animation follows that path.
Instead the code simply chains together the 3 separate animations. Since we might care about iOS 3, we don’t use animation blocks, but instead use begin/commit animation methods and register an animation stop selector where we start the next animation.
Just like we did when animating the menu in, at each step we animate both the menu as well as the cell to give the illusion that the cell content is pushing the menu out of view.
UPDATE: It was pointed out on Hacker News that the Twitter app actually puts the menu behind the cell and then only animates the cell content in and out. The menu isn’t animated at all. I’ve updated the code so that by default it now does this style of animation. If you really liked the pushing behavior where both the menu and cell content are animated, there is a PUSH_STYLE_ANIMATION #define that you can set to YES to get it back.
Full Source code: https://github.com/boctor/idev-recipes/tree/master/SideSwipeTableView
Sometimes it’s useful to quickly see all the subviews of a UIView.
Perhaps you’re debugging a problem in one of your views or trying to understand the inner workings of one of the built in views.
You can simply iterate over a view’s subviews, but then you won’t see subviews deeper than one level. You need a method that recursively walks the hierarchy.
Luckily Apple has already done this with the recursiveDescription method, part of a UIDebugging category on UIView.
recursiveDescription was recently documented in a tech note titled iOS Debugging Magic.
At the gdb prompt in the Xcode debugger you can say:
(gdb) po [[self view] recursiveDescription]
and instantly see a description of the entire view hierarchy. We used this in our Transparent UIWebViews recipe to figure out the view hierarchy of a UIWebView:
(gdb) po [webView recursiveDescription] <UIWebView: 0x68220e0; frame = (0 0; 320 460); > | <UIScrollView: 0x4b2bee0; frame = (0 0; 320 460); > | | <UIImageView: 0x4b2dca0; frame = (0 0; 54 54); > | | <UIImageView: 0x4b2da20; frame = (0 0; 54 54); > | | <UIImageView: 0x4b2d9c0; frame = (0 0; 54 54); > | | <UIImageView: 0x4b12030; frame = (0 0; 54 54) > | | <UIImageView: 0x4b11fd0; frame = (-14.5 14.5; 30 1); > | | <UIImageView: 0x4b11f70; frame = (-14.5 14.5; 30 1); > | | <UIImageView: 0x4b11f10; frame = (0 0; 1 30); > | | <UIImageView: 0x4b11eb0; frame = (0 0; 1 30); > | | <UIImageView: 0x4b11e50; frame = (0 430; 320 30); > | | <UIImageView: 0x4b2d0c0; frame = (0 0; 320 30); > | | <UIWebBrowserView: 0x6005800; frame = (0 0; 320 460); >
Full Source code: https://github.com/boctor/idev-recipes/tree/master/VerticalSwipeArticles
The Reeder iPhone App lets you pull up to see the title of the next article. If you pull up far enough the arrow rotates and the next article animates into view. We want to recreate this UI.
If you don’t pull up far enough, the article bounces back into view and that is a very strong clue that we are dealing with a UIScrollView.
A UIScrollView is used to display content that is larger than the application’s window. You tell it the contentSize of your content and it manages scrolling within the content. When you get to the edge of the content, the scroll view bounces to let you know you’ve reached the edge.
Normally when you pull up at the edge of a scroll view empty space appears, but in the Reeder app, the title of the next article appears along with an arrow. To recreate this we’ll create a scroll view with a contentSize that is the same as the scroll view. Then we’ll tell the scroll view to alwaysBounceVertical. This causes a view that bounces vertically when you pull up or down.
Next we’ll add a header view as the subview of the scroll view and set it’s frame to be right above the scroll view and we’ll add a footer view as the subview of the scroll view and set it’s frame to be right below the scroll view. The header and footer are offscreen but when you pull up or down, they get pulled into view.
In addition to trying to figure out how to recreate the feature, we need to also figure out how to structure our code. The most reusable part of this feature is the ability to swipe up and down to see another article while seeing a preview of the previous/next article. We’ve already determined that we’ll be using a scroll view that we’ve customized so it seems logical that we would create a subclass of UIScrollView.
The arrows in the header and footer rotate to let the user know that when they lift their finger, the previous/next article will be shown. When the view has been scrolled past some distance we need to trigger this arrow rotation.
To accomplish this we will listen to the UIScrollViewDelegate’s scrollViewDidScroll message and check the scroll view’s contentOffset. This means that our UIScrollView subclass will have itself as its delegate. This sounds odd but works just fine.
Our subclass will send out 4 messages:
A header/footer is loaded when the user pulls down or up past the height of the header/footer. It is unloaded when they pull back and hide part of the header/footer. So with one arrow image, this is how we animate the arrow rotation:
- (void) rotateImageView:(UIImageView*)imageView angle:(CGFloat)angle
{
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.2];
imageView.transform = CGAffineTransformMakeRotation(DegreesToRadians(angle));
[UIView commitAnimations];
}
-(void) headerLoadedInScrollView:(VerticalSwipeScrollView*)scrollView
{
[self rotateImageView:headerImageView angle:0];
}
-(void) headerUnloadedInScrollView:(VerticalSwipeScrollView*)scrollView
{
[self rotateImageView:headerImageView angle:180];
}
-(void) footerLoadedInScrollView:(VerticalSwipeScrollView*)scrollView
{
[self rotateImageView:footerImageView angle:180];
}
-(void) footerUnloadedInScrollView:(VerticalSwipeScrollView*)scrollView
{
[self rotateImageView:footerImageView angle:0];
}
The UIScrollViewDelegate’s scrollViewDidEndDragging message lets us know when the user has lifted their finger after dragging. To animate the next page, we place the page below the footer and inside of an animation block place it on screen. This results in a nice up animation.
if (_footerLoaded) // If the footer is loaded, then the user wants to go to the next page
{
// Ask the delegate for the next page
UIView* nextPage = [externalDelegate viewForScrollView:self atPage:currentPageIndex+1];
// We want to animate this new page coming up, so we first
// Set its frame to the bottom of the scroll view
nextPage.frame = CGRectMake(0, nextPage.frame.size.height + self.contentOffset.y, self.frame.size.width, self.frame.size.height);
[self addSubview:nextPage];
// Start the page up animation
[UIView beginAnimations:nil context:nextPage];
[UIView setAnimationDuration:0.2];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:@selector(pageAnimationDidStop:finished:context:)];
// When the animation is done, we want the next page to be front and center
nextPage.frame = self.frame;
// We also want the existing page to animate to the top of the scroll view
currentPageView.frame = CGRectMake(0, -(self.frame.size.height + headerView.frame.size.height), self.frame.size.width, self.frame.size.height);
// And we also animate the footer view to animate off the top of the screen
footerView.frame = CGRectMake(0, -footerView.frame.size.height, footerView.frame.size.width, footerView.frame.size.height);
[UIView commitAnimations];
// Increment our current page
currentPageIndex++;
}
We also register a callback for when this animation is done and make sure our header and footer are in place for the next time the user pulls the scroll view up or down.
Our UIScrollView subclass calls the delegate’s viewForScrollView:atPage to get the actual pages. Life would be simple if we could return a static page like say an image, but in the real world it is more likely that you will be returning a UIWebView to accommodate things like titles that may wrap.
The sample app uses a JSON feed of the top paid apps in the App Store and uses a UIWebView to display each page.
No matter how simple the html that you are displaying in a UIWebView, the rendering will not be instantaneous and there will always be an overhead of setting up the UIWebView. If every time viewForScrollView:atPage is called you created a new UIWebView with html, then as this page is getting animated into view, the rendering will not have completed. The net result will be that the scroll animation will show a blank white page instead of the actual content.
To deal with this the sample app keeps around a previousPage and nextPage UIWebViews. When asked for page 1, the sample preloads previousPage with page 0 and nextPage with page 2. If there are other caching techniques you think would work here, please share your thoughts in the comments.
Full Source code: https://github.com/boctor/idev-recipes/tree/master/VerticalSwipeArticles