Your browser (Internet Explorer 6) is out of date. It has known security flaws and may not display all features of this and other websites. Learn how to update your browser.
X
Post

Removing pesky UITableView lines

You know how, if you have a UITableView with only a few entries, how the divider lines for the empty (non-existent) cells still show up? This happens to me every once in a while, and every time I always have to go look up the solution. Well in case you’re wondering, it’s as easy as one line of code:

mTableView.tableFooterView = [[[UIView alloc] initWithFrame:CGRectZero] autorelease];

And that’s it. Happy coding!

Post

Slide-Away Screens

In an app I’m working on now I thought I’d give the slide-away screen metaphor a try. Like the one found in the Path and Facebook apps. So I went online and sought a library that could do that for me. Many of the libraries I found were actually for the iPad, and mimicked the behaviour of the Twitter app. Close, but not what I wanted. I found a few libraries, but many of them didn’t support swiping. They were only controlled by a button or some other event.

Finally I found DDMenuController, which seemed to fit what I wanted to do. I experimented with it a bit, and it was easy enough to use. I could also tweak the source code to change how much of the “underneath” controller would show. This was important because I only wanted to show a narrow strip of controls, which isn’t very wide. But soon enough I discovered bugs. Often on a swipe, the underneath controller wouldn’t show at all, but rather it would just be black. I fiddled with the source code a bit and finally got that to happen a lot less often. But when I added a second underneath controller on the right side, the problem came back with a vengeance, and at that point I just gave up.

I looked around again, and found a library I didn’t find the first time. It’s called ViewDeck. So far, I haven’t encountered any bugs, which is good. But I haven’t yet found a way to change how far the main screen moves over. Well, that’s not quite true. I did find something for that, but when I set it to a value I like, the main screen just bounces back and doesn’t stay in the slid-over position. So I’m going to play around with it a little bit and post an update with what I found.

UPDATE 1: I found the solution. It’s just a matter of making sure the rightLedge is large enough to trigger the left edge to stay in place. I’m hoping this works out later when I’ll want a small left underneath view and a large right underneath view.

UPDATE 2: As it turns out, trying to have a large right view was problematic. But I changed the source code to fix that. In (void)panned:(UIPanGestureRecognizer*)panner there’s a divsion by 3.0. I changed that to 4.0 and that made it work.

Post

Remove Tab Bar When Controller Pushed

Sometimes it’s the little tips that prove really useful, and as such, I present one today. This would have helped me out greatly about three months ago. If you have a UITabBarController, and you’re pushing a new controller in a UINavigationController which is one of the tabs, typically the tab bar across the bottom stays in place. But you can remove it quite easily with this line of code:

controller.hidesBottomBarWhenPushed = YES; // yay! this hides the tab bar!
[self.navigationController pushViewController:controller animated:YES];

I just discovered this recently, and have already made use of it. Good luck.

Post

Pattern for Loading Table Data

I gave this pattern to a coworker of mine the other day, and they thought it was quite helpful. Starting iPhone develops may be just getting used to how things work on the iPhone, so I thought I’d point out a good way to load a lot of data into a table view.

- (void) finishStartup
{
// this must be done on main thread
// stop spinner here using self.view
[mTableView reloadData];
}

- (void) startup
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

// load the data here

[pool release];

[self performSelectorOnMainThread:@selector(finishStartup) withObject:nil waitUntilDone:NO];
}

- (void)viewDidLoad {
[super viewDidLoad];
// start spinner here using self.view
[self performSelectorInBackground:@selector(startup) withObject:nil];
}

You have to remember that GUI stuff like starting and stopping spinners and reloading the table view have to be done on the main thread.

You can’t just do something like this:

- (void)viewDidLoad {
[super viewDidLoad];
// start spinner here using self.view
// load data
// stop spinner
[mTableView reloadData];
}

Why? Because the spinner never gets a chance to be processed and shown. That’s a general pattern whenever you want to show the spinner. You have to start it in the main thread and do your processing independently.

Post

Custom Button in NavBar

Just a quick snippet of code for you guys today.

Ever wanted to know how to put a custom image into the navigation bar? It’s actually pretty easy. Create a UIButton object, and stick that into the UIBarButtonItem object. The trick is to remember to set the size:

UIButton *add = [UIButton buttonWithType:UIButtonTypeCustom];

image = [UIImage imageNamed:@"button_plus.png"];
[add setImage:image forState:UIControlStateNormal];
image = [UIImage imageNamed:@"button_plus_selected.png"];
[add setImage:image forState:UIControlStateSelected];

add.bounds = CGRectMake(0, 0, image.size.width, image.size.height);
[add addTarget:self action:@selector(onAdd) forControlEvents:UIControlEventTouchUpInside];

addButton = [[UIBarButtonItem alloc] initWithCustomView:add];
self.navigationItem.rightBarButtonItem = addButton;

And Bob’s your uncle!

Post

iPhone QRCode Integration

For one of my jobs I had to integrate QRCodes into the app. A QRCode is a 2 dimensional barcode, like you see here:

qrcode

There are several ways of generating QRCodes online, including this one for generating MeCards, and this one for generating text, URL, SMS, and phone number ones.

Integrating QRCodes into an iPhone app is pretty easy, because there’s already an open source project that does it. It’s called ZXing (or Zebra Crossing).. It’s cross platform and includes Android and other versions too. We just need to concern ourselves with the iPhone version.

Included in the project is a bunch of actions too, depending on what is encoded in the QRCode. It’ll attempt to parse the code and generate an email address or a MeCard or one of several others things as well. All the action happens in the DecoderViewController class, and presentResultForString is the method you want to look at to see what it does with the results of the image scan.

You can actually use your iPhone to take snapshots of QRCodes right from your computer’s monitor. I’ve used this method to test my app, but frankly it’s not always reliable. As well, it works a lot better with the 3GS camera than the 3G camera, because it’s just better. :)

Post

Fading an Image into Another

My Pollen project required that I be able to have an image, and fade it into another image. Other projects since then have also had similar requirements. The effect is very nice, and the great thing is that the code is really quite simple. Just have two images, and do an animation from one to the other. I like have one image always being the active one (when not animating), so that’s how I structured the code below. I assume you have two UIImageView’s set up named mFrontView and mBackView.

UIImage *newImg = // get new image here;
UIImage *oldImg = mFrontView.image;

if ( newImg != oldImg )
{
        [mBackView setImage:mFrontView.image];
               
        mFrontView.alpha = 0;
        mBackView.alpha = 1;
        [mFrontView setImage:newImg];
               
        [UIView beginAnimations:nil context:NULL];
        [UIView setAnimationDuration:0.5];
        mFrontView.alpha = 1;
        mBackView.alpha = 0;
        [UIView commitAnimations];
}

Post

Fixing Broken Pipes

pipewrenchThere’s nothing like the simulator for putting your app together quickly. It’s better than debugging on the device for one main reason: it’s fast! Starting up the app is fast. Debugging the app is fast. Everything is fast.

But sometimes you just need to slow down. By using the device. Of course, I was testing on the device today and made a critical error.

I couldn’t figure out what was wrong. I was getting error messages in the debugger console like this:

putpkt: write failed: Broken pipe

Or this:

mem 0×1000 0x3fffffff cache
mem 0×40000000 0xffffffff none
mem 0×00000000 0x0fff none

Or even like this:

Sent: [1251990710.449:32] +
Sent: [1251990710.449:32] Hc-1
Recvd: [1251990710.454:32] OK
Sent: [1251990710.455:32] qC
Recvd: [1251990710.460:32] QC0
Sent: [1251990710.460:32] qStepPacketSupported
Recvd: [1251990710.463:32] OK
Sent: [1251990710.478:49] QEnvironment:SHELL=/bin/bash
Recvd: [1251990710.481:49] OK
Sent: [1251990710.481:49] QEnvironment:TMPDIR=/var/folders/UF/UFCJNauIGPu+F7L7bsqhZU+++TI/-Tmp-/
Recvd: [1251990710.485:49] OK
Sent: [1251990710.485:49] QEnvironment:Apple_PubSub_Socket_Render=/tmp/launch-o19tpZ/Render
Recvd: [1251990710.488:49] OK
etc…..

It was quite frustrating.

So I looked around on the forums, and there were several questions about these errors, many of which went unanswered. (Forums can only get you so far sometimes.) But eventually I found the answer: you can’t debug your program if you’re using an ad hoc profile.

So I used the appropriate profile, and now I debug to my heart’s content. iPhone development seems to be straining with this kinds of gotchas. I just hope I remember this solution next time I find this problem. Writing it down will help, I hope! :)

Post

Images in a Scroll View

I know that when I was a beginning iPhone developer doing things that seem so simple now weren’t so simple back then. Just because I didn’t know any better and was unaware of the tools and API’s available to me.

One thing that I’ve been doing a lot recently is putting several images in a UIScrollView, so I thought I’d post the barebones version of the code here in case anyone finds it useful. Hopefully I’ll be able to add more snippets in the future.

#define IMAGE_WIDTH   320
#define IMAGE_HEIGHT  416
       
- (void)viewDidLoad
{
    [super viewDidLoad];
       
    NSArray *photos = nil;      // TODO – fill with your photos
       
    // note that the view contains a UIScrollView in aScrollView
       
        int i=0;
        for ( NSString *image in photos )
        {
                UIImage *image = [UIImage imageNamed:[photos objectAtIndex:i]];
                UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
                imageView.contentMode = UIViewContentModeScaleAspectFit;
                imageView.clipsToBounds = YES;
               
                imageView.frame = CGRectMake( IMAGE_WIDTH * i++, 0, IMAGE_WIDTH, IMAGE_HEIGHT);
               
                [aScrollView addSubview:imageView];
                [imageView release];
        }
        aScrollView.contentSize = CGSizeMake(IMAGE_WIDTH*i, IMAGE_HEIGHT);
        aScrollView.delegate = self;
}