May 29, 2009

Trapping the UINavigationBar Back Button (part 3)

I have covered the topic of trapping the back button in order to do something. These previous forays are part 1 and part 2. I had implemented my solution from part 2, and it is working well. However, I am always on the look out for a better, cleaner solution. Just recently, an anonymous commenter pointed out something I had not noticed before: the UINavigationBarDelegate prototype has a method called navigationBar:shouldPopItem: It is called just before the item is popped. If it returns NO, then the pop won't actually happen. But this can be the perfect way to insert some logic before the pop happens (and return YES at the end). Here is how you would implement your back-button press event logic:
- (BOOL)
navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item
{
   //insert your back button handling logic here

   // let the pop happen
   return YES;
}  
This is so much cleaner than my previous solution. I love it! Thanks to whomever pointed out the obvious.

May 20, 2009

A Background Task Class

I am working on an app that makes XML calls to a web service. At first, I made the calls within the main thread and waited for the results to come back. Not surprising, when these calls took longer than usual, the user interface suffered. Basically, the UI locks up while waiting for a response from the web service. This is because the main thread is busy and cannot service UI-related events, such as touch events. What I had to do was make the web service calls in a separate thread so that the main thread can continue to service the UI. I coded up a few uses and I quickly discovered that my app wanted two different behaviors while making web service calls in the background. These behaviors are:
  1. The background task should operate transparently. It would only alert the user if the operation timed out,
  2. The user had to wait for the operation to complete, but user is given the option to cancel the background operation.
I decided to create a utility class to encapsulate these behaviors and make it all a little easier. Thus I created the BackgroundTask class, which I present here. What does it do? The BackgroundTask class is like a NSThread class; it allows you to spin off code in a separate thread. But this class is more powerful because it supports the two behaviors described above.

The Behaviors

First, let's discuss the desired behaviors in a little more detail. Behavior #1 listed above is for tasks that are low priority and don't require the user to wait for it to complete. The task is kicked off in the background, and the main thread retains control for the user to do whatever they need. They don't even know there is something going on in the background (except for the spinning network access icon in the status bar). The only feedback the user may get is if the operation timed out, meaning it failed. Each task created is given a timeout value. If the operation is not complete by this timeout period, then an alert message pops up that displays something like this:
The alert message displayed below "Operation failed" is also configurable when you create the task object. Behavior #2 is more of a blocking operation, but with the option to cancel it if the user feels it is taking too long. This is useful for higher-priority tasks that require the user to wait until it is complete. For example, if the user hit a refresh button to update the items displayed in a table, it kind of makes sense for the refresh to complete before continuing. When running a task like this, it displays a modal dialog like this:
I added a little optimization to this behavior as well. This dialog won't pop up for X number of seconds, where X is configurable. This is to avoid those situations where the background task completes almost immediately. When this happens, it was silly for the alert to pop up then disappear so fast that the user couldn't even read what it said. So the alert will only come up if the task takes longer than X seconds.

Instantiating

Since the BackgroundTask class supports two different behaviors, it also supports two different initializers. This first one initializes the object for behavior #1, where the task runs the background without any user interaction:
BackgroundTask* task = [[BackgroundTask alloc] initWithTarget:self
   selector:@selector(executeEmailPage:)
   argument:myparent.page
   timeout:40.0
   alertMsg:@"Attempt to email page timed out"]; 
The target/selector arguments specify the method to run in the new thread. The argument is optional data you can pass to the above method. Timeout specifies how long to give that thread before putting up an alert. And the final argument is the alert message to put up when the operation times out. This next initializer is for behavior #2, where the user can cancel the operation:
BackgroundTask* task = [[BackgroundTask alloc]
   initWithTarget:self
   selector:@selector(backgroundRefresh)
   argument:nil
   waitBeforeAlert:0.5
   title:@"Refreshing data"
   msg:@"Standby while refreshing pages"]; 

The target, selector, and argument parameters are the same as the first initializer. The waitBeforeAlert parameter specifies how long to give the operation to complete before putting up the modal box. The title and msg parameters allow you to configure the text displayed in the modal dialog.

Usage

To use the BackgroundTask object after you create it is very simple:
// start the background thread
[task start];

// release it since we don't need it anymore
[task release]; 

Getting The Code

The code for the BackgroundTask class is available on the Osmorphis google group. You can download the .zip archive here. Hopefully its usage is clear enough that you can easily stick the code in your own project and begin using it right away.

May 12, 2009

Multiple Buttons on a Navigation Bar

Previously, I wrote about how to use standard icons in your navigation bar. This takes advantage of the nav bar's ability to put a button on the right side. But what if you want to put multiple buttons there? If your title is short enough, you can easily fit 2 or 3 buttons in that space. The nav bar allows you to stick content on the right hand side via the rightBarButtonItem property. On the surface, you might think you can add but one button to this property. But it's not so. There is an initializer for UIBarButtonItem that takes a view:
initWithCustomView:(UIView *)customView
With this, you can create multiple UIButton objects, add them to a UIView, and then create a UIBarButtonItem passing in this parent UIView. The following pseudo-code shows what I mean:
// create the container
UIView* container = [[UIView alloc] init];

// create a button and add it to the container
UIButton* button = [[UIButton alloc] init...];
[container addSubview:button];
[button release];

// add another button
button = [[UIButton alloc] init...];
[container addSubview:button];
[button release];

// now create a Bar button item
UIBarButtonItem* item = [[UIBarButtonItem alloc] initWithCustomView:container];

// set the nav bar's right button item
self.navigationItem.rightBarButtonItem = item;
[item release];

But, what if you want multiple *standard* buttons. Access to the standard buttons/icons is through the UIBarButtonItem class, not UIButton. You can't add UIBarButtonItem objects to a UIView. While there is support to create a UIBarButtonItem object from a UIView-derived object, there does not seem to be a way to do the opposite; there is no way to create a UIView object from a UIBarButtonItem. The only way I found how to deal with multiple UIBarButtonItem objects is to stick them in a toolbar. A UIToolBar is designed to hold multiple UIBarButtonItem objects. Think of a toolbar as your container, instead of the UIView object. And since a UIToolBar is UIView based, it can be added to the right side of a nav bar using the above trick. The following code shows how you can add two standard buttons to the right side:

// create a toolbar to have two buttons in the right
UIToolbar* tools = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, 133, 44.01)];

// create the array to hold the buttons, which then gets added to the toolbar
NSMutableArray* buttons = [[NSMutableArray alloc] initWithCapacity:3];

// create a standard "add" button
UIBarButtonItem* bi = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:NULL];
bi.style = UIBarButtonItemStyleBordered;
[buttons addObject:bi];
[bi release];

// create a spacer
bi = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
[buttons addObject:bi];
[bi release];

// create a standard "refresh" button
bi = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh target:self action:@selector(refresh:)];
bi.style = UIBarButtonItemStyleBordered;
[buttons addObject:bi];
[bi release];

// stick the buttons in the toolbar
[tools setItems:buttons animated:NO];

[buttons release];

// and put the toolbar in the nav bar
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:tools];
[tools release];


May 6, 2009

Reflection in Objective-C

When I first learned Java, I thought the whole reflection package was novel, and it put Java closer to the realm of interpreted scripting language and farther away from the then-mainstream languages of C and C++. The idea of being able to look inward at the class metadata of an object at runtime was really fascinating, although not always terribly useful in everyday application programming. Those programmers coming over to the Objective-C world from Java will be happy to know that Objective-C also supports the concepts of reflection, AKA introspection. In fact, Objective-C 2.0 has many runtime features such as dynamically changing a class definition and even creating a new class dynamically. The necessity for these abilities is rather obscure, and it firms my belief that Objective-C is a ridiculously bloated language. To me, Objective-C has a bit of an identity crisis: it's not quite sure if it's an interpreted language or a compiled language. The runtime is mostly dynamic. Unlike C++, Objective-C does runtime binding. This is why you can do things like define methods for an implementation that are never declared in your header file, or extend existing classes with categories. Unfortunately, because of all this bloat, it is hard to find just the simple stuff that you might actually use in your day-to-day code. I will try to unearth some of these jewels and bring them to light.

Grandpa NSObject

Most, if not all, the runtime reflection support comes from the NSObject class. NSObject, like Java's Object class, is the granddaddy class of all classes (except for some rare exceptions). Therefore, all of your classes already have access to this reflection support. It's important to note that all this reflection support is not part of the Objective-C language per se; it is part of the NS* runtime environment. This is also why some of this stuff seems tacked on. It's because it has, uh, been tacked on.

Getting Class Metadata

You can access the class metadata for an object by calling the class method:

Class c = [self class];

This method is both an instance and a class method. It returns a C struct full of all sorts of mysterious stuff, like the list of instance variables, methods, etc. All of this is old hat to the java.lang.reflect package users out there, but the interfaces to reach this stuff in Objective-C seems really complex. This is probably difficult by design to keep the riff-raff out. So far, the only use I have for the class method is to provide the parameter to the isKindOfClass method described below. I never need to actually look at the contents of the Class structure.

Dynamic Method Invocation

I already discussed one aspect of reflection in my article about invoking methods. This allows you to build up a method call, arguments and all, at runtime. This is similar to using the java.lang.reflect.Method class in Java.

Testing inheritance

Java has an operator called instanceof that allows you to check if a object is an instance of a particular class or interface. Objective-C has a similar feature, the isKindOfClass: method. isKindOfClass: will return YES if the receiver is an instance of the given class or an instance of any class derived from the class. For example, if I had an array of related pointers, I can operate on each differently according to its type:

for(BaseClass* base in myArray) {
 if([base isKindOfClass:[ClassOne class]]) {
     // do stuff specific to ClassOne
 }
 else if([base isKindOfClass:[ClassTwo class]]) {
     // do stuff specific to ClassTwo
 }
 else if([base isKindOfClass:[ClassThree class]]) {
     // do stuff specific to ClassThree
 }
 // etc
}

If you want an exact class match, and not match any inheriting classes, then you use isMemberOfClass:.

Testing Protocol Conformance

Similar to class instance testing, you can also test whether an object conforms to a particular protocol. Java neatly uses instanceof for both classes and interfaces, but Objective-C has to have a more clunkier approach. To test conformance, use the conformsToProtocol: method:

BOOL conforms = [obj conformsToProtocol:@protocol(MyInterface)];

Testing for Method Existence

To an old Java and C++ hack like me, it seems extremely odd that you may not know if an object implements a method or not. But since Objective-C classes are largely dynamic, you need a way to check if a method you need is actually there. Hence the method respondsToSelector:. Here you are asking the receiver if it implements (or inherits) the given method:

if([obj respondsToSelector:@selector(aMethod:)]) {
   // it's there, so we can call it
   [obj aMethod:YES];
}

Certainly, there is way more you can do on the reflection front in Objective-C, but I tried to discuss the more commonly used reflection mechanisms. If you need too add hardcore dynamic nature to your software, you should be familliar with these documents: Runtime Programming Guide: Introduction Runtime Reference

May 4, 2009

Using Standard Toolbar and Navbar Buttons

In an article several months back, I discussed how to use standard Tab bar buttons. These are icons provided by Apple for you to use in the tab bar. But tab bars are not the only container that offers system-provided icons. There are standard icons available for toolbars and navigation bars. Recently I wanted to make use of a system icon, but it took me some time to find what icons were standard and how to use them. Even though it is fairly trivial, I would like to share this information, if nothing else but to save people a few minutes of needless searching. If you feel weird about using system-provided icons, don't. Apple is very keen on developers making use of the standard icons and encourages it in the HIG. However, Apple is also adamant about the consistent use of these icons. If your application uses a standard icon in a way unintended for that icon, then it is likely your app will get rejected from the app store. For example, if you are using the standard Contacts icon to bring up a list of contact lens providers, ding! Here is the section in the HIG that lists the standard icons and their intended uses. This gives you an idea to what is available, but doesn't help you coding one of them up. You have to know how to reference each of these in your code. For this, you have to look at the UIBarButtonItem class reference. If you go to the Constants section, you will see the UIBarButtonSystemItem enumerations which define the values for all the system icons.

For nav bars

To put a system icon on the navigation bar, you have several choices for the location. At the most basic level, you can put the icon on the left or the right. If you require more refinement, it's quite easy to do with subviews, but that technique is well-documented and outside the scope of this article. Here is the simple code for putting the standard refresh icon in the left position:
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc]
                                      initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh
                                      target:self
                                      action:@selector(refresh:)];

The first parameter is the enum from the UIBarButtonItem reference. The target/action parameters specify the method to call when the button is hit.

For toolbars

Adding the standard icons to a toolbar is a little more involved, but still straight-forward. Toolbars also take UIBarButtonItem objects, but instead of assigning them to specific locations, you must add them to an NSArray object first (in the order you want them) and then add the array to the toolbar object.

// create the toolbar object
UIToolBar* toolbar = [[UIToolBar alloc] init];

// create an array to hold the buttons
NSMutableArray* buttons = [[NSMutableArray alloc] init];

// create a standard button
UIBarButtonItem* button = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh
target:self
action:@selector(refresh:)];

// stick it in the array
[buttons addObject:button];

// add more buttons
//...

// add the array to the toolbar
toolbar.items = buttons;

// done with the buttons array
[buttons release];