views:

918

answers:

2

I'd like to override UILabel's setText method but I'm not sure that A) it's possible and B) if maybe there's a better way to achieve what I'm trying to accomplish.

I have a subclass of UIView that has a UILabel property as one of its subviews. I'd like to know when the UILabel's "text" property changes so I can adjust the size of the rounded rect behind it. If I owned UILabel i'd just override setText...but UILabel is Apple's and its implementation is hidden. So, how should I be going about this?

+2  A: 

You can use Key-Value Observing to track changes to the UILabel.text property. The approach involves three steps:

1) Registering to observe the property, when you load the view

[label addObserver:inspector
             forKeyPath:@"text"
                 options:(NSKeyValueObservingOptionNew |
                            NSKeyValueObservingOptionOld)
                    context:NULL];

2) Receiving a notification about any changes:

- (void)observeValueForKeyPath:(NSString *)keyPath
              ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context
{
    if ([keyPath isEqual:@"text"]) {
        // put your logic here
    }
    // be sure to call the super implementation
    // if the superclass implements it
    [super observeValueForKeyPath:keyPath
                ofObject:object
                 change:change
                 context:context];
}

3) De-registering the observation whenever you aren't interested any more:

[label removeObserver:inspector forKeyPath:@"text"];
notnoop
A: 

Are you just using a rounded rectangle as the background for the Label? If that is the case, you can look into using UIIMage stretchableImageWithLeftCapWidth:topCapHeight. This will take an image you've created that has a left and top repeating section with a width you specify and automatically stretch it to your width.

If not, Key-Value observing is the way to go. Just to cover another option--this is like "playing with fire," as Apple programmer Evan Doll said in one of his Stanford lectures--you can use method swizzling to exchange one method implementation for another.

void method_exchangeImplementations(Method m1, Method m2);

In this case, you want to tweak the implementation of setText, but you also want to call the original setText in UILabel. So you could exchange setText with setTextAndUpdateSize, and inside setTextAndUpdateSize do what setText does originally plus add on a little more. If you are confused or think this is a bad idea, it probably is. You can get a Method object to pass into method_exchangeImplementations by calling class_getInstanceMethod([NSSTring class], @selector (methodName).

Once your method swizzle has been called once, inside your new method you can then call the old implementation of setText from within the new one by using, yes, setTextAndUpdateSize. It's confusing and not recommended, but it works. A good example can be found in the developer sample code.

stereointeractive.com
This is an interesting approach that I wasn't aware of...but in this case I think I'll stick with the mainstream... good to know though. thx.
Meltemi
Subclassing and is a much easier way of overriding a method, unless you can't control the class of the UILabel.
Jesse Rusak