As some of you may know, I have been building an IPhone app for hair stylists to help them manage their clients.
Anyway, as a part of this project, I wanted to display a message that would slide down from the top of the window, hang out for a bit, then fade out.
I wanted the code that presents this message view to be as simple (and as few lines/characters of code, Objective-C is sooo verbose!!!) For example:
MessageController *msg = [ [ MessageController alloc ] initWithMessage:@"Good luck buddy, cannot log in!" ofType:kMessageTypeWarning hideAfterDelay:3 ];
[ msg show ];
[ msg release ];
As you can see I want to call the show method, then immediately call release.
The results of this call:

Well this took me some figuring out, because I’m still new to cocoa touch and all of its framework goodness.
However, this ended up pretty straight forward, but there was a gotcha I ran up against. The important bits were:
- Create a custom init method.
- In the ‘show’ method, retain yourself!
- In the routine that removes the subview, release yourself.
1. Create a custom init method
This ended up important and was a gotcha for me. Initially, I thought using ‘initWithNibName:bundle’ would suffice. What I found is that my ‘show’ method would be called before all of my outlets were hydrated from the NIB (they were still nil!).
The solution to this was to create my custom init method, and call ‘loadNibNamed:owner:options’. After that call, all my outlets were hydrated and I was able to use/configure them in my ‘show’ method.
-( id )initWithMessage:( NSString * )message ofType:( MessageType )messageType hideAfterDelay:( NSTimeInterval )delay
{
self = [ self init ];
if( self )
{
if( ![[ NSBundle mainBundle ] loadNibNamed:@"MessageView" owner:self options:nil ] )
NSLog( @"Failed to load nib, should use better error handling" ); //todo: fix this
self.messageType = messageType;
self.message = message;
self.delay = delay;
}
return self;
}
2. In the ‘show’ method, retain yourself!
The important work in this method is to first and foremost to retain yourself, as the caller will most likely calling ‘release’ immediately after calling the ‘show’ method. After that, I use the remainder of this method to configure my UIView, using my outlets. **Remember, every retain must be balanced with a release!
-( void )show;
{
[ self retain ]; // retain self;
lMessage.text = self.message; // set outlet text
if( self.messageType == kMessageTypeWarning )
{
lMessage.textColor = [ UIColor whiteColor ]; // set outlet text color
NSString *path = [ [ NSBundle mainBundle ] pathForResource:@"bg-warning" ofType:@"png" ]; // find the path to the background image for the view
UIImage *img = [ [ UIImage alloc ] initWithContentsOfFile:path ]; // create my image
ivBackground.image = [ img stretchableImageWithLeftCapWidth:9 topCapHeight:6 ]; // set it to my image view outlet
[ img release ]; // release my image
}
else
{
// removed for clarity, more or less identical to the code above
}
UIWindow *window = ( ( ColorClientAppDelegate * )[ UIApplication sharedApplication ].delegate ).window; // get a reference to my window
// create CGRects for the start location and final positions of my view.
CGRect offScreen = CGRectMake(0, self.view.frame.size.height *-1, self.view.frame.size.width, self.view.frame.size.height );
CGRect onScreen = CGRectMake(0, 19, self.view.frame.size.width, self.view.frame.size.height );
self.view.frame = offScreen; // set the initial state of the view offscreen
[ window addSubview:self.view ]; // add the view to the window
// Animate the movement of the view onto the screen.
[ UIView animateWithDuration:.7 animations:^(void) {
self.view.frame = onScreen;
} ];
[ self performSelector:@selector( removeMessageFromWindow ) withObject:nil afterDelay:( self.delay + .7 ) ]; // Call the method that will remove this view from the window, after the delay plus the time it took for our reveal animation to play
}
In the last line of this method, I used delayed execution to execute the ‘removeMessageFromWindow’ selector. This is the method, which will take care of balancing the ‘[ self retain ]’ call we made earlier. Let’s look at it.
3. In the routine that removes the subview, release yourself.
-( void )removeMessageFromWindow
{
[ UIView animateWithDuration:.5 animations:^(void) {
self.view.alpha = 0;
} ];
[ self performSelector:@selector( performMessageRemoval ) withObject:nil afterDelay:.5 ];
}
-( void )performMessageRemoval
{
[ self.view removeFromSuperview ];
[ self release ];
}
I suppose I need a little explanation here, the ‘removeMessageFromWindow’ actually just animates the fade-out of the view. Again, I use delayed execution to call another method after the fade out animation has enough time to play. The ‘performMessageRemoval’ actually removes the current view from the window and then balances out the retain, with a call to ‘[ self release ]’.
Well, that was it, pretty straight forward stuff I think.