mkc842
mkc842

Reputation: 2381

How to create a reusable button

I'm new to Xcode and objective c. I want to create a button (probably a UIBarButtonItem, for a navigation bar) with a particular appearance, which I will use repeatedly in different views. I've searched at length but can't figure out how.

Would it be appropriate to subclass UIBarButtonItem? I tried to do that, but I was quickly in over my head. Once I create the .h and .m files as a subclass of UIBarButtonItem, do I then have to instantiate a UIBarButtonItem? Do those files not automatically create a button object for me (imported from the parent class), which I can refer to as self? It seems like it would be weird to instantiate a button within its own subclass.

One thing I want to do is add the line,

button.titleLabel.lineBreakMode = NSLineBreakByWordWrapping;

but I'm at a loss as to how to create reusable button with that property.

Even if that is completely the wrong approach to creating a reusable custom button, I clearly need to improve my understanding of objects, so explanation of my misunderstandings would be much appreciated!

Please?

Upvotes: 1

Views: 2679

Answers (1)

foundry
foundry

Reputation: 31745

You can do this without subclassing - by making a category (a preferred way of doing things in Objective-C). With a category you can provide custom methods for an object without having to subclass it. You can't (easily) provide custom properties, but in your case this is not relevant.

Using a Category

This is how your category header file could look:

//  UIButton+StyledButton.h

#import <UIKit/UIKit.h>

@interface UIButton (StyledButton)
- (void) styleButton; 
@end

Then in the implementation file:

//
//  UIButton+StyledButton.m
//

#import "UIButton+StyledButton.h"

@implementation UIButton (StyledButton)

- (void) styleButton {
    //style your button properties here
    self.titleLabel.lineBreakMode = NSLineBreakByWordWrapping;
}

('self' refers to the button object, which also acquires the custom methods you write in the category.)

To use it, #import "UIButton+StyledButton.h" then you can do this sort of thing...

on viewDidLoad {
    [super viewDidLoad];
    UIButton* myButton = [[UIButton alloc] initWithFrame:myFrame];
    [myButton styleButton];
}

Using a Subclass

The subclassed equivalent would look something like this:

The header file...

//  MyCustomButton.h

#import <UIKit/UIKit.h>

@interface MyCustomButton : UIButton
- (id)initWithCoder:(NSCoder *)coder;
- (id)initWithFrame:(CGRect)frame;
@end

The implementation file...

//  MyCustomButton.m

#import "MyCustomButton.h"

@implementation MyCustomButton

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        [self styleButton];
    }
    return self;
}

- (id)initWithCoder:(NSCoder *)coder
{
    self = [super initWithCoder:coder];
    if (self) {
        [self styleButton];
    }
    return self;
}

- (void) styleButton {
    //style your button properties here
    self.titleLabel.lineBreakMode = NSLineBreakByWordWrapping;
}

You provide two init methods - initWithFrame is the method to call when alloc/initing the object in code; initWithCoder is the init method called by the system if loading the object from a storyboard or xib.

To create one of your custom buttons in code, you alloc/init as you would any other object:

MyCustomButton* button = [[MyCustomButton alloc] initWithFrame:buttonFrame];

You wouldn't also alloc/init the superclass instance, this is done by the initWithFrame: method in the subclass when it calls [super initWithFrame:frame]. self refers to your custom subclass instance, but that includes all of the (public) properties and methods from it's superclass - unless you have implemented overrides in the subclass.

To use your subclassed button in a storyboard/xib, just drag out a regular button then set it's type to your custom button class in the Identity Inspector. The initWithCoder method is called automatically when the button is loaded from the storyboard/xib into a view.

update

From your comments, you seem to harbour a few confusions still, so here are some highly compressed de-obfuscating notes...

Keep away from subclassing UINavigationController unless you really know what you are doing. It's rarely necessary.

The buttons on a navController's interface are properties of it's contained viewControllers. Look up the navigationItem property of UIViewController (similarly - in the case of a UIToolbar - the View Controller has a toolbarItems property). This allows Navigation Controllers to be context-aware.

The 'viewDidLoad' in my example is assumed to be in a regular UIViewController. My example is also a category on the regular UIBUtton which has no formal relationship with UIBarButtonItem.

Try getting a UIButton category to work with a regular ViewController first before experimenting with UIBarButtonItem (which does not inherit from UIButton).

UIBarbuttonItem has no initWithFrame, because the thing that organises the bar (UINavigationBar or UIToolbar) - in this case a Navigation Controller - is responsible for it's ultimate size and positioning. The viewController governs the relative order of barButtonItems, and whether they appear on the left or the right, and the content and (some aspects of) it's appearance, but the rest is up to the NavController.

Upvotes: 9

Related Questions