8
\$\begingroup\$

I have the following class:

MyProtocolClass.h

#import <Foundation/Foundation.h>

@protocol MyExampleProtocol <NSObject>
@required
- (void)someRequiredMethod:(NSInteger)someValue;
@end

@interface MyProtocolClass : UIView

// interface stuff

@end

MyProtocolClass.m

#import "MyProtocolClass.h"

@interface MyProtocolClass()

@property (nonatomic,assign) id<MyExampleProtocol> delegate;

@end

@implementation MyProtocolClass

// implementation stuff

- (IBAction)fooMethod:(id)sender {
    [self.delegate someRequiredMethod:0];
}

- (IBAction)barMethod:(id)sender {
    [self.delegate someRequiredMethod:1];
}

- (IBAction)fooBarMethod:(id)sender {
    [self.delegate someRequiredMethod:2];
}

@end

And this is a fairly simple example. The problem here is that I have a view with three buttons, and each button has it's own method, which simply calls a method on the delegate with an index to tell the delegate which button was pressed.

Surely there's a way to simplify this code and improve its readability. A more complex view with several more interactive UI elements would quickly make this file very large if we have to add another 4 lines to the file for each interactive UI element we add to the screen.

\$\endgroup\$
11
\$\begingroup\$

You're right. There is a simpler way.

First of all, instead of giving every interactive element its own method, let's give them all the same method.

- (IBAction)buttonPressed:(id)sender;

If they're not all buttons, a different method name is in line, but for this example, I'll assume them all to be buttons.

Now then, whether you're creating these UI elements programmatically or via interface builder, the next important step is to give each element its own unique tag. In the Interface Builder, this is done under the "Attributes Inspector" (the icon that looks like a shield). The "tag" field is the second field under the "View" heading. It doesn't matter what tag you give the button. It only matters that the tags are unique.

Setting the tag programmatically is as simple as this:

myButton.tag = 100;

Now, instead of a different method for each button (or other UI element), we can simply use a single button:

- (IBAction)buttonPressed:(id)sender {
    [self.delegate someRequiredMethod:sender.tag];
}

Now we've condensed your code. But we can do even better.

Let's create an enum so that we're not just sending arbitrary numbers, but instead we send values that the delegate can make more sense of (and if using a switch, Xcode will help the delegate be sure he accounts for every case).

typedef NS_ENUM(NSInteger, MyExampleButtonEnum) {
    ButtonIndexUnknown = 0,
    ButtonIndexTopLeft = 100,
    ButtonIndexTopRight = 101,
    ButtonIndexBottomLeft = 102,
    ButtonIndexBottomRight = 103
};

This should be placed in the .h and before our @protocol. Then we can change our protocol to look like this instead:

@protocol MyExampleProtocol <NSObject>
@required
- (void)someRequiredMethod:(MyExampleButtonEnum)button;
@end

Now we just have to set our button tags to match these enum values. The good news is, if we're doing it programmatically, we can use the enum itself to set the values. For example...

UIButton *topLeftButton = [[UIButton alloc] initWithFrame:someFrame];
topLeftButton.tag = ButtonIndexTopLeft;
[topLeftButton addTarget:self action:@selector(buttonPressed:)
    forControlEvents:UIControlEventTouchUpInside]

And now we slightly modify our buttonPressed: method as such:

- (IBAction)buttonPressed:(id)sender {
    if (sender) {
        [self.delegate someRequiredMethod:sender.tag];
    } else {
        [self.delegate someRequiredMethod:ButtonIndexUnknown];
    }
}

And our delegate can implement the delegate method in some fashion like this:

- (void)someRequiredMethod:(MyExampleButtonEnum)button {
    switch(button) {
        case ButtonIndexTopLeft:
            // do stuff
            break;
        case ButtonIndexTopRight:
            // do stuff
            break;
        case ButtonIndexBottomLeft:
            // do stuff
            break;
        case ButtonIndexBottomRight:
            // do stuff
            break;
        case ButtonIndexUnknown:
        default:
            // do stuff
            break;
    }
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.