iOS Quick Tip – Avoid combining animations with setMasksToBounds, if possible

For Random Dice Roller, I wanted to have pretty looking buttons with rounded corners. If you google for something like ‘ios custom button with rounded corners’, you’ll find various examples that use setMasksToBounds and a corner radius, like this:

[button.layer setMasksToBounds:true];
[button.layer setCornerRadius:4.0f];

This was my initial approach, and it worked flawlessly. My button corners were perfectly rounded.

However, when I added animations into the mix, things didn’t quite work out so well. There was a noticeable lag when animating my screens full of buttons with modal-segue-style transitions. I tracked it down to the call to setMasksToBounds. With setMasksToBounds set to false, the animations were liquidy smooth, as expected. But turn it on, and things get choppy.

I didn’t want to release an app with noticeably choppy animations, so I started looking for a solution. Turns out that it’s ridiculously easy to solve the problem – just use a transparent .PNG with the corners already cut out!

Check out my sample project to see the difference in performance.

If you run it on the simulator, it will appear to work fine; run it on your device, though – in my case an iPod Touch 4g – and you will see some significant choppiness.

The moral of the story is that setMasksToBounds can cause performance issues, and if you can avoid it, you should.

iOS Quick Tips – ‘A signed resource has been added, modified, or deleted’

While trying to run my latest app on my device, I kept encountering a very strange error. Xcode would pop up with a message box saying

‘A signed resource has been added, modified, or deleted’

Huh? What? In typical Xcode fashion, the error message doesn’t actually tell you anything about what’s wrong or how to fix it. It turns out that the problem was caused by having special character in the product name – in my case, a ?. Removing the ? from the product name fixed the problem. But I really wanted that question mark; the app wouldn’t be the same without it! Not to worry, because you can set the bundle name (the name that gets shown on your device’s home screen) independently of the product name, and the bundle name can contain special characters.

So, let this be a warning: don’t put any special characters in your product name – only put them in your bundle name!

 

As an aside, the issues with broken links on the blog should now be fixed. Sorry for the inconvenience!

iOS Quick Tips – Fading in a UIView with a transparent background, but not transparent contents

For March’s (slightly delayed) app, I wanted to fade in a UIView. Easy enough, right? Well, the catch was that I wanted the background to be partially transparent, but I didn’t want the contents of the view to be transparent.
My first attempt was simply to animate the view’s alpha value from 0 to 1:

view.alpha = 0;

[UIView beginAnimations:@"fade in" context:nil];
[UIView setAnimationDuration:3.0];
view.alpha = 0.5;
[UIView commitAnimations];

This didn’t quite do the trick; although the view’s background was transparent, so were its contents. Oops!

After doing some searching, I found a StackOverflow post which suggested to use colorWithAlphaComponent to set the view’s background color, instead setting the view’s alpha. So here was attempt #2:

[view setBackgroundColor:[[UIColor blackColor] colorWithAlphaComponent:0]];

[UIView beginAnimations:@"fade in" context:nil];
[UIView setAnimationDuration:3.0];
[view setBackgroundColor:[[UIColor blackColor] colorWithAlphaComponent:0.5]];
[UIView commitAnimations];

This wasn’t quite right either. The view’s contents appeared fully opaque as soon as the view was displayed, before the background had faded in!

So, to fix this problem, all we have to do is use a combination of the methods. Use colorWithAlphaComponent to fade in the view’s background, and use alpha to fade in the contents of the view:

[view setBackgroundColor:[[UIColor blackColor] colorWithAlphaComponent:0]];
label.alpha = 0;

[UIView beginAnimations:@"fade in" context:nil];
[UIView setAnimationDuration:3.0];
label.alpha = 1.0;
[view setBackgroundColor:[[UIColor blackColor] colorWithAlphaComponent:0.5]];
[UIView commitAnimations];

Success!

Of course, you can’t actually see the fade in animation in the screenshots, so here’s a sample project that demonstrates the problems and the solution.
Download the Sample Project

iOS Quick Tips: Why can’t I compare two numbers?!?

Recently, I was trying to debug some code that wasn’t working properly. The code looked something like this:

if(someNumber > 0)
{
   // Do some really important stuff
}

… but even when someNumber was supposed to be 0, the block was always being entered. What the heck? I can’t even compare two numbers properly? I smacked my forehead when I realized that someNumber was actually a NSNumber*, not an int, and what I really wanted to do was this:

if(someNumber.intValue > 0)
{
   // Do some really important stuff
}

Of course, this wasn’t flagged as a warning by the compiler, because as is typical with the C family of languages, checking if a pointer (or an id) is greater than 0 is a perfectly valid operation.

I guess the moral of the story is that if some basic operation isn’t working, take a step back and make sure that your initial assumptions are valid!

iOS Quick Tips: Version control and backups with DropBox and Git

Git is an amazing VCS. However, Git by itself is not a backup if you are only using it locally – if your hard drive dies, so does your data. Fortunately, Git’s flexibility lets us do some really cool things, such as push and pull from a ‘remote’ repo on the same machine. When combined with a distributed storage system such as Dropbox, it becomes the perfect tool for the solo developer.

Here’s how you go about using Dropbox with Git. This little tutorial assumes that you already have a local Git repository (perhaps created by Xcode).

Step 1 is to install Dropbox on your Mac. After the install is complete, you’ll notice that you now have a Dropbox folder in your home directory.

Everything you put in this folder will be magically backed up in the Dropbox cloud.

Step 2 is to create a bare Git repository for your project in the Dropbox folder. You can do this from Terminal with the following command, after CD-ing into the Dropbox folder:

git init –bare MyProject.git

This creates the Git repository that your work will be pushed to.

Step 3 is to link your existing Git project with your new Dropbox’d Git repository. After CD-ing into the directory containing your project, issue the following command to add your new Dropbox repository as a ‘remote’ for your project:

git remote add origin ~/Dropbox/MyProject.git

Step 4 is to push your changes to your new ‘remote’ repository:

git push origin master

That’s it! Your local Git repository is now synced with your Dropbox folder. Not only does this give you a safe, distributed backup, but it makes it easy to work on your projects from any machine.

Happy developing!

iOS Quick Tip – programmatically hiding sections of a UITableView with static cells

As we have seen previously, getting a UITableView with static cells to show up can be a bit tricky. You need to remove some of the table methods. However, if you want to dynamically show and hide sections at runtime, you will need add one of them back in and put in a little bit of extra effort.

We’ll start out with a static UITableView that looks like this:

We’ll then add actions for the button touch events, which will set a flag indicating whether or not to hide the middle section:

- (IBAction)HideButtonTouch:(id)sender
{
    hideTableSection = YES;
    [self.tableView reloadData];
}

- (IBAction)ShowButtonTouch:(id)sender
{
    hideTableSection = NO;
    [self.tableView reloadData];
}

We then have to re-implement our previously removed method tableView:numberOfRowsInSection to return the correct number based on whether or not the middle section should be shown:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    if(section == 1)
    {
        if(hideTableSection)
            return 0;
        else
            return 1;
    }
    else
    {
        return 2;
    }
}

This gets us closer, but the section header is still showing:

The next thing we have to do is make sure the header doesn’t show up. We do it by overriding theset two methods. Note that we are returning 1 in heightForHeaderInSection – if we return 0, it won’t work.

-(UIView*)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
    if(hideTableSection && section == 1)
        return [[UIView alloc] initWithFrame:CGRectZero];
    return nil;
}

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
    if(section == 1 && hideTableSection)
        return 1;
    return 32;
}

This gets us most of the way there, but it looks like the gap in between the two sections is a little too large. We can get around this by messing with the size of the footer in a similar manner:

-(CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section
{
    if(section == 1 && hideTableSection)
        return 1;
    return 16;
}

-(UIView*)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section
{
    if(section == 1 && hideTableSection)
        return [[UIView alloc] initWithFrame:CGRectZero];
    return nil;
}

Voila! Our section is now properly hidden.

I’m sure this isn’t the ideal way to show and hide sections, but it works well enough for my purposes. And since it took a little bit of effort to figure out, I wanted to share it. Enjoy!

Download the Demo Project