Some notes on UIView animation

UIView animation is a simple and nice way to add to your user experience. I just wanted to point out a few suggestions when using UIView animations.

Duration (speed):
If you choose to use animation to compliment some of the stuff already happening in UIKit, either at the same time or before/after, it makes a big difference how fast your animations are. Pretty much all the UIKit animations I have come across have a duration of 0.3f and so should yours. Of course, it’s doesn’t always work 100% but for the most part, 0.3f is what you should aim for. It’s quick so your user don’t wait for something to finish animating before continuing with the next input action, and it’s not too fast so that the user doesn’t have a chance to see where the object came from or what happened.

If you have an animation happening while the keyboard animates up or down, use an animationDuration of 0.3f. Same with pushing and popping the navigationController. Annotations in MKMapView also drop at a duration of 0.3f.

0.3f is the way to go.

A simple UIView animation can be added with the following code:

[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.3f];
self.segmentControl.alpha = 1.0f;
[UIView commitAnimations];

The above example is from an app I’m doing, where the segmentControl is enabled and I increase its visibility in the toolBar at the same rate as a pin drops in the map within the same screen.

When to use animation:
A few objects come with free animation (also at a duration of 0.3f, of course). For example, when adding a UIBarButton to your UINavigationBar, consider setting these with animation. If you replace a UIBarButton with another, they’ll even animate in and out nicely during the change. When adding a pin to a map, why not drop it onto the map with an animation, instead of it suddenly appearing on a map from nowhere?

Another good advice is to do animation (whether your own or with objects that include animations) to bring attention to an object. For example, if you have a pushed view, consider what you can “add” after the view has appeared through animating your objects in viewDidAppear.

Create a better UX with animation
Consider all the ways you can use UIView animation blocks in your app to enhance the user experience. It’s a great way to create a more fluid and pleasant experience for your users. A user’s inputs and actions will feel less rough and more smooth and soft to the touch. Don’t go overboard with animations. Too many will become annoying and it’s important to use animations only where appropriate.

The best advice is probably to have a look at many of the built in apps designed by Apple as well as the many free animations that a part of UIKit objects (how UIBarButtons animate in and out when you push a UIViewController stack, how a modal view appears from the bottom, etc.).

UITabBarController and UIActionSheet – 65% less hit point!

Ever noticed an app where the UIActionSheet’s bottom button doesn’t want to respond to your taps, unless you hit exactly in the top of the button? If you have a UITabBarController and want to display a UIActionSheet, you have to be careful with what view you show the UIActionSheet in.

If a UIActionSheet is shown in “self” or “self.view” and you got a UITabBarController behind it less than half of the last button will respond to taps:

UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:@"UIActionSheet Title" delegate:nil cancelButtonTitle:@"Cancel" destructiveButtonTitle:nil otherButtonTitles:@"Option 1", @"Option 2", nil];
actionSheet.actionSheetStyle = UIActionSheetStyleBlackTranslucent;
[actionSheet showInView:self];
[actionSheet release];

Here’s how much of the button will actually respond to taps:

Here’s what you need to do to fix it:

[actionSheet showInView:[[[UIApplication sharedApplication] windows] objectAtIndex:0]];

And what it does:

Linking *directly* into your app’s reviews in the App Store [UPDATED]

UPDATED 10/3:

I have received some feedback regarding this post that I thought I would share:

One developer implemented this feature in a UIAlertView pop-up, asking users nicely if they’re enjoying the app and whether they would like to be taken to the App Store to review the app. This UIAlertView happens after the 10th app launch, so you’re already dealing with people who have used your app enough that they must be liking it. It’s a $0.99 app by the way.

In just two short hours after implementing this, he had received 6 new app store reviews, all with positive feedback along with 4 or 5 stars, nothing less!

What can we learn from this?

1. People are, by nature, lazy. Consider the steps required to actually reviews an app that you like: You have to actively find it again in the App Store, scroll down and go to the review page. Then tap the button to submit your own review, login, etc. Unless someone’s super excited or very pissed off about your app, you will only see a small percentage of people taking their time to go through all those steps to review and rate your app. By asking users, who’ve been using your app more than just a few times nicely for a review, then linking *directly* to the App Store, they’re already skipping many of the above steps and rating and reviewing your app becomes a quick activity related to an app they’re enjoying.

2. Reviews are generally positive. Ratings on the other hand tend to be more black and white. Either people hate your app, delete it and quickly give it 1 star. I doubt a lot of people give apps 4-5 stars when they delete an app. They’re deleting it for a reason; either they didn’t like it, or it just wasn’t what they needed/expected. If they like the app, chances are they’d never delete it, and you wouldn’t be getting those great 4-5 star ratings (refer to #1). I have seen reviews that have pointed out several wrongs about apps, but they’re still adding a 4-5 star rating with the review. It may seem there’s a difference in people’s mind about a review and a rating. I think a lot of users think of reviews as a way of communicating to the developer that there’s an issue with the app, or they want this or that added, but they still use and love the app regardless (giving it a high rating).

3. You’re asking the right users. When deleting an app, Apple has implemented a horrible UIAlertView asking the same people who just got rid of your app to rate it. I think we all agree this is ridiculous (refer to #2). With a UIAlertView inside your app for 10+ launches linking into the reviews of your app in the App Store, you can pretty much assume these people are enjoying the app, especially if they agree to review it. If they’re don’t like it that much or don’t have time, it’s just a simple tap to dismiss the UIAlertView.

ORIGINAL:

Today @coffeeandiphone we briefly discussed ratings/reviews for apps, and one person mentioned he’d like to show some of the users who have used his app more than X amount of times an alert where he’d ask them to review his app in the app store.

So here’s a link that will take people directly to the review section of your app in the App Store. Note, though, they won’t be able to go “Back” to the actual page for your app in the App Store (when on a device), so they can’t really see that they’re on the review page for your app. Therefore, you probably want to make it veryclear where your linking to and before sending them out of your app, into the App Store.

http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?id=341136260&pageNumber=0&sortOrdering=1&type=Purple+Software&mt=8

See where is says “?id=341136260” ? That’s the only thing you need to change. Just insert your own app ID there and you’re good to go. Don’t get confused by “type=Purple+Software&mt=8”, because if you change that, it won’t work for some reason. I am not quite sure why it has to say Purple Software, but I don’t really care as long as it works :)

Combine it with something like this (“Fighting Back Against The App Store’s Negative Rating Bias”) and you’re good to go!

Edit: I can’t remember where I got this info from, but credit goes to whoever/wherever, of course.

Colouring fun with moreNavigationController

When adding more than 5 view controllers to your UITabBarController, a “More” tab is automatically setup for you, which includes a view controller and even a modal view for letting the user edit the app’s tabs in the order they want.

By default, the navigation bars for both the moreNavigationController and the modal view (edit) are the Default blue, but changing these colours isn’t exactly straightforward.

Here’s an example of what we want to achieve:

I use an orange colour in one of my apps for the navigation bars and it just looks wrong when the more tab’s navigation bars are blue.

Changing the colour of the moreNavigationController is quite easy. After you alloc the UITabBarController, set the colour of the moreNavigationController:

tabBarController.moreNavigationController.navigationBar.tintColor = [UIColor orangeColor];

You can also use the barStyle property if you like:

tabBarController.moreNavigationController.navigationBar.barStyle = UIBarStyleBlackOpaque;

That wasn’t so hard. But the navigation bar for the modal view that pops up when the user taps “Edit” is still the default blue. So let’s change that as well:

First, if you haven’t already, make sure your AppDelegate implements the UITabBarControllerDelegate. Then add the optional delegate method willBeginCustomizingViewControllers: in your AppDelegate’s implementation file, and add the following lines of code:

– (void)tabBarController:(UITabBarController *)controller willBeginCustomizingViewControllers:(NSArray *)viewControllers {
    UIView *editView = [controller.view.subviews objectAtIndex:1];
    UINavigationBar *modalNavBar = [editView.subviews objectAtIndex:0];
    modalNavBar.tintColor = [UIColor orangeColor];
}

Again, it’s possible to set the barStyle property instead of the tintColor, but barStyle won’t give you all the colour options, of course.

Now that you have gained control of the modal view, you change more properties. By default, the navigation bar title says “Configure”, but you can change that as well, or how about the background colour? Here’s how:

– (void)tabBarController:(UITabBarController *)controller willBeginCustomizingViewControllers:(NSArray *)viewControllers {
    UIView *editView = [controller.view.subviews objectAtIndex:1];
    editView.backgroundColor = [UIColor grayColor];
    UINavigationBar *modalNavBar = [editView.subviews objectAtIndex:0];
    modalNavBar.tintColor = [UIColor orangeColor];
    modalNavBar.topItem.title = @"Edit Tabs";
}

Regarding the HIG… I am not sure if all this is allowed. I will be submitting an update with an orange coloured navigation bar for both those views, and I believe other apps have it (NY Times) so I don’t think it goes against the HIG.

However, I don’t think it’s a good idea to change the backgroundColor property of the “editView”. I tried with a grey colour and it doesn’t look right. It might also get your app rejected, because it’s such a big change. That your navigation bars are the same colour throughout your app only makes it look better in my opinion, rather than having a blue navigation bar clash with the rest of your beautifully designed app :)

Dedicated Static Analyzer Debug Configuration in Xcode

I went to iPhone Tech Talk in Toronto yesterday, and among the many sessions, I attended Michael Jurewitz’ (@jurewitz) session, “Testing and Debugging Your iPhone Application.” He covered a lot of the basics for using Instruments (very similar to last year’s session on that), how to setup provisioning, etc. for Ad-Hoc Beta testing, etc.

He also covered Xcode’s new built in Static Analyzer (Clang), which is incredibly useful and powerful now that it’s a part of Xcode. However, I haven’t been using it that much (he said to use it at least once a week), but he briefly showed that there’s actually a setting in the project’s build settings called “Run Static Analyzer,” which is a checkbox and checking this will run the Static Analyzer every time you build your project. Jurewitz mentioned you could make a duplicate Debug build configuration that is dedicated to running the Static Analyzer.

He went over this very fast, so I think a lot of people missed the benefit of this great tip!

So here’s how you do it:

Open your project settings and go to the Configurations pane. Pick your Debug configuration (or whichever build configuration you want to dedicate to running the Static Analyzer) and hit Duplicate in the bottom of the window. I called mine “Debug with Clang,” so I know it’s my Debug build configuration.

Next, hop into the Build pane and find “Build Options”. Within those options you’ll find the “Run Static Analyzer” option. Check the checkbox and you’re good to go!

Now, every time you build your project using your new “Debug with Clang” it’ll automatically analyze your project. Personally it’ll probably help me remember to run the Static Analyzer way more often on my project, instead of just once in a while.

Keep in mind that running the Static Analyzer increases the time it takes to build your project, so don’t choose that build setting if you’re just testing new code, etc. Also, I find it’s a good idea to “Clean All Targets” once in while, as it seems to ‘reset’ the Static Analyzer, because otherwise I am finding that it tends to miss certain errors on the second, third, fourth, etc. time you build with the “Run Static Analyzer” option on.

Shadow offset in custom UITableViewCells (for the inner OCD in you)

Apple has a great example code for drawing fast UITableView using custom, complex UITableViewCells called AdvancedTableViewCells. In the example code, you get three versions and I prefer the one named CompositeSubviewBasedApplicationCell, because it draws your cell as one view, just like Loren Brichter’s Fast Scrolling example.

However, Apple’s example code goes a bit further than Loren’s code and adds example for drawing images, different coloured backgrounds and also has better code for handling highlighted cells in my opinion.

As great as the code is, they left out one small detail that really adds a bit extra to the look of your UITableView, plus I find it makes the text less blurry and easier to read. It only requires a few lines of code for each piece of text you’re drawing. The example code provided is actually from the App Store, which, if you look on your device has the nice white shadow y offset.

Here are the before and after pictures:

BEFORE

AFTER

Original code:

_highlighted ? [[UIColor whiteColor] set] : [[UIColor blackColor] set];
[_cell.name drawAtPoint:CGPointMake(81.0, 22.0) withFont:[UIFont boldSystemFontOfSize:17.0]];

Drawing the shadow:

CGPoint point = CGPointMake(81.0, 23.0);
_highlighted ? [[UIColor clearColor] set] : [[UIColor colorWithWhite:1.0 alpha:0.3] set];
[_cell.name drawAtPoint:point withFont:[UIFont boldSystemFontOfSize:17.0]];
point.y -= 1;
_highlighted ? [[UIColor whiteColor] set] : [[UIColor blackColor] set];
[_cell.name drawAtPoint:point withFont:[UIFont boldSystemFontOfSize:17.0]];

There are a couple of things to note in the above code. Firstly, I made a CGPoint from the original code and made the Y location one pixel lower. This is because we’re drawing the shadow first, underneath the original text. After the shadow has been drawn, we just tell the point to move up one pixel and the text will be drawn in the original location from the original code.

Second, you’ll need to set the text colours twice. Note that for the shadow, we need to use [UIColor clearColor] so it doesn’t look weird when highlighted. Also, set the alpha low (you need to check with your cell’s background colour), but make sure it’s not too white. It’ll be really subtle, yet noticeable to anyone who appreciate nice UI design.

Of course it doesn’t work with white cells, but if you’re doing any kind of custom UITableViewCell drawing it’s a nice touch. Here’s how I use it in my app:

Translucent UINavigationBar

Apparently, making your UINavigationBar requires a (tiny) bit more code than just:

self.navigationController.navigationBar.barStyle = UIBarStyleBlackTranslucent;

After trying hard to change my UINavigationBar colour, and mostly giving up, I found out you have to set its tintColor to nil.

self.navigationController.navigationBar.tintColor = nil;
self.navigationController.navigationBar.barStyle = UIBarStyleBlackTranslucent;

Getting visual with UITableViews [UPDATED]

UPDATED 27/8:

I’ve updated the code and pushed it to bitbucket. All the drawing takes place in a separate class and only one subview is added to the cell(s). Again, if you need to add images for every single cell in a large UITableView, you should probably update the code a bit to suit your needs :)

$ hg clone http://bitbucket.org/runmad/visual-uitableview/
ORIGINAL:

I am working on an app that lists movie releases and tapping a title will display more details about the selected release in a UITableView. One of the details include number of discs included with the product.

Instead of just writing the number of discs in the detailTextLabel, I thought it would be nice to visualize the number of discs. Where appropriate, it’s always a nice touch to use icons/images to dispay information, instead of just using text:

There’s two things added to the cell: a UIImage and a UILabel with the disc amount and label number updated according to the number of discs in the product.

The example code can of course be used for a lot of things than just display some disc icons and a number. Since they’re subviews, it’s probably not wise to use the code as is for every single cell in a large UITableView. Since I am just displaying the icons in one cell, I do not have any memory issues when using subviews.

You can use any integer for the discCount, in the example project, I just used the row number.

A nice touch is the spacing for the disc and label for every disc. The more discs, the less spacing there is between the images and labels (minus one pixel). You will also notice that when there’s 10 or more discs, I simply write the disc amount in the cell’s detailTextLabel and just add one disc image (without a number label). That makes for easier reading when there’s a large amount of discs.

Let me know if you use the code in your own project! I’d love to hear from you if you make any updates to the code that you’d like to share.

cell.textLabel.text = @"Discs";
UIImage *discIcon = [UIImage imageNamed:@"iconDisc.png"];
int discCount = indexPath.row + 1;
if (discCount < 10) {
    for (int i=0; i) {
        CGRect frame = CGRectMake(83 + (17 – discCount) * i, 11, 24, 27);
        UIImageView *discCellImageView = [[UIImageView alloc] initWithFrame:frame];
        discCellImageView.image = discIcon;
        [cell.contentView addSubview:discCellImageView];
        int discNumber = 1 + i;
        CGRect labelFrame = CGRectMake(86 + (17 – discCount) * i, 8, 24, 27);
        UILabel *discCountLabel = [[UILabel alloc] initWithFrame:labelFrame];
        discCountLabel.textColor = [UIColor darkGrayColor];
        discCountLabel.font = [UIFont systemFontOfSize:8];
        discCountLabel.shadowColor = [UIColor whiteColor];
        discCountLabel.shadowOffset = CGSizeMake(0, 1.0);
        discCountLabel.backgroundColor = [UIColor clearColor];
        discCountLabel.text = [NSString stringWithFormat:@"%d", discNumber];
        [cell.contentView addSubview:discCountLabel];
    }
} else if (discCount >= 10) {
    cell.detailTextLabel.text = [NSString stringWithFormat:@"%d", discCount];
    CGRect frame = CGRectMake(113, 11, 24, 27);
    UIImageView *discCellImageView = [[UIImageView alloc] initWithFrame:frame];
    discCellImageView.image = discIcon;
    [cell addSubview:discCellImageView];
}

You can download the project here: http://bitbucket.org/runmad/visual-uitableview/.

Or in Terminal:

$ hg clone http://bitbucket.org/runmad/visual-uitableview/

Thanks to Blake for his input and help!