3

I have read multiple times that we should not subclass a component (a UIButton for example) :

The problem is when I use Interface Builder. For example, I have a button with a precise appearance in a lot of my views. I can set them each time with IB (it's painful), or I can use a custom class to factorize the custom behavior and appearance.

It seems a bit contradictory to me that the only way to simplify the process with IB is to do it the way that everybody recommends against.

Is there a better solution ? Can I use a category with IB ?

Thanks.

Community
  • 1
  • 1
Matthieu Oger
  • 823
  • 1
  • 11
  • 20

3 Answers3

3

You might be able to use the UIView appearance proxy. I don't know what all you're doing to your buttons but this might help:

Put this is your AppDelegate file in the application:didFinishLaunchingWithOptions: method

if([UIButton conformsToProtocol:@protocol(UIAppearanceContainer)]){
    [[UIButton appearance] setBackgroundImage:[UIImage imageNamed:@"YourImage"] forState:UIControlStateNormal];
    //modify any other UIButton properties you want to set globally
}
hgwhittle
  • 9,316
  • 6
  • 48
  • 60
2

I don't see any reason that you shouldn't subclass UIButton, especially for your purpose of making configuration with IB easier. Neither of the links you provided explain why you shouldn't subclass, so their assertions don't seem reliable. On the other hand, the presence of UIButtonTypeCustom in UIButton.h gives the impression that the framework authors planned for UIButton subclasses.

Caleb
  • 124,013
  • 19
  • 183
  • 272
2

The second link you provided was pretty clear, and this is pretty much what apple itself states, subclass, but never mess with the internal structure.

Best example is iOS 7, now things are completely different and, for example, an application I'm maintaining had a subclassed UIControl, and now it has trouble running on the new iOS, simply because, it was built with assumptions on how the internal structure works (iterating the internal subviews replacing some things). You might not get your app rejected, but it will be a pain in the a** to maintain.

As a rule of thumb, anything you can do to an UIButton from the outside, something like this:

[myButton setBackgroundImage:... forState:...];
[myButton setTextColor:... forState:...];
myButton.titleLabel.font = ...

You can move it to the inside of a custom subclass method:

+ (UIButton*)fancyPantsButton
{
    UIButton *button = [UIButton butonWithType:UIButtonTypeCustom];

    [myButton setBackgroundImage:... forState:...];
    [myButton setTextColor:... forState:...];
    myButton.titleLabel.font = ...

    return button;
}

You can also do this on init or awakeFromNib without problems (and I usually prefer the later).

UIAppearence is also an option, as was suggested by user hw731. Whatever floats your boat, really.

As for the second question, nib files pretty much create instance a class and then fill-in the things it stores using setValue:forKey: when loading (that's why you get an error like "class is not key-value compliant for something" when you screw up a nib), so if something is categorised when the nib is being loaded, then yes, nibs respect categories, as its simply using initWithCoder.. and then filling in the gaps.

And, by the same token, the nib file won't be able to fill-in custom properties, since it doesn't know about them, unless you explicitly add them on the "User Defined Runtime Attributes" in IB (iOS 5 onwards).

Another technique for nibs, is using

@property (strong) IBOutletCollection(UIButton) NSArray *buttons;

And then iterating and customising buttons accordingly (be it via a subclass, category, local method, ...). This method is really helpful if you want just a handful of custom buttons, but not enough to warrant using a subclass.

Can
  • 8,502
  • 48
  • 57