Custom Title Label in a UINavigationController

I had a requirement while working on Toast that the titleView in our UINavigationController should use a custom UIFont in the label.  That means one has to create their own titleView on the navigationController’s navigationItem.

This in itself is not a big problem (in viewDidLoad or wherever):

// now set your own label here
UILabel *titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 120, 44)];
titleLabel.font = [UIFont museoSlab:MuseoSlabType500 size:18];
titleLabel.textColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"topnav-background-text-gradient.png"]];  // also cool!  can have gradient colors
titleLabel.shadowColor = [UIColor colorWithWhite: 1.0 alpha:0.59];
titleLabel.shadowOffset = CGSizeMake(0, 1.0);
titleLabel.text = self.title;
titleLabel.backgroundColor = [UIColor clearColor];
titleLabel.textAlignment = UITextAlignmentCenter;

self.navigationItem.titleView = titleLabel;

but what happens if you set your title after you set this label?  I’ll tell you.  Nothing.  If you change the UIViewController’s title property, you also have to go find this titleView and change his .text property.

Enter Observer:  We can just observe the title property and write a handler for what should happen should it change:


// In your MyCustomViewController.m that will use custom titleView Labels

static void *kObjectStateObservingContext = &kObjectStateObservingContext;

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {

        [self addObserver: self forKeyPath:@"title" options:NSKeyValueObservingOptionNew context: kObjectStateObservingContext];
    }
    return self;
}

- (void)dealloc
{
    // just to be safe and avoid a leak
    [self removeObserver: self forKeyPath:@"title"];
}

#pragma mark KVO Methods

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    // you should also ideally check that [keyPath isEqualToString: @"title"]
    if ( context == kObjectStateObservingContext ) {

        // here we update the text of the titleView
        if ([self.navigationItem.titleView isKindOfClass:[UILabel class]]) {

            [(UILabel*)self.navigationItem.titleView setText: self.title];
        }

    }
    else {
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
}

Another simple recipe. As is typical for intermediate level programmers, I have to admit there is a bit going on that I can’t explain to you, but I know that if you do it, the net result is good. -.-

Advertisements

5 thoughts on “Custom Title Label in a UINavigationController

  1. Great.Is this a optional solution?

    We can override the setTitle: method of UIViewController class and set the title in titleLabel. We need to declare the titleLabel as a private property here.

    • I’m not exactly sure what you’re asking. Plus this post is quite old and I wouldn’t assume any of this will ‘just work’ on iOS 7. Furthermore, one might be able to use the UIAppearance API to set these everywhere in the app with just a few lines of code.

    • You would have to make sure to check self.isViewLoaded or else setting the title could induce the view being loaded prematurely.

  2. This will probably work most of the time, but it’s not technically safe. UIViewController.title is not documented as being compliant with Key-Value Observing, so it may break at any time. To avoid this, you could override -[UIViewController setTitle:]

    • Great comment! Thank you. Out of curiosity, where would it usually state whether a property is KVO observable or not? I’m guessing they are not going to suddenly make title non-observable, as on their side the property is probably synthesized anyway. But you are correct. There is a chance this could break.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s