Clicky

iOS Dev Nugget 91 Using Event Delivery Responder Chain for Global Actions

.

Need to run a code review on your codebase? Hire me

In an app, you might have the same action available in several UIViewController or UIView that is triggered via a user tapping on a button. For e.g. there might be multiple places in your app with a “Send Feedback” button or a reveal menu button. You might have code like this:

@implementation SomeViewController1

- (void)setUp {
    [btn addTarget:self action:@selector(menuTapped) forControlEvents:UIControlEventTouchUpInside];
}

- (void)menuTapped {
    //Many
    //lines
    //of
    //code to set up and display menu
}

@end

@implementation SomeViewController2

- (void)setUp {
    [btn addTarget:self action:@selector(menuTapped) forControlEvents:UIControlEventTouchUpInside];
}

- (void)menuTapped {
    //Many
    //lines
    //of
    //code to set up and display menu
}

@end

After repeating this in a few classes, you decided that duplicate code is bad, so you refactor it into:

@implementation SomeViewController1

- (void)setUp {
    [btn addTarget:self action:@selector(menuTapped) forControlEvents:UIControlEventTouchUpInside];
}

- (void)menuTapped {
    [[UIApplication sharedApplication] menuTapped];
}

@end

@implementation SomeViewController2

- (void)setUp {
    [btn addTarget:self action:@selector(menuTapped) forControlEvents:UIControlEventTouchUpInside];
}

- (void)menuTapped {
    [[UIApplication sharedApplication] menuTapped];
}

@end

@interface AppDelegate : NSObject

@implementation AppDelegate

- (void)menuTapped {
    //Many
    //lines
    //of
    //code to set up and display menu
}
@end

You move the common logic of setting up and display the menu into the AppDelegate class and cut down on duplicate logic.

However, touch events are propagated upwards the responder chain if the target is nil and the responder chain ends with the UIApplication delegate1. So, if we pass nil instead of self as the target when setting up the action on UIButton, the event will ultimately be propagated to our UIApplication delegate.

@implementation SomeViewController1

- (void)setUp {
    [btn addTarget:nil action:@selector(menuTapped) forControlEvents:UIControlEventTouchUpInside];
}

@end

@implementation SomeViewController2

- (void)setUp {
    [btn addTarget:nil action:@selector(menuTapped) forControlEvents:UIControlEventTouchUpInside];
}

@end

@interface AppDelegate : NSObject
@end

@implementation AppDelegate

- (void)menuTapped {
    //Many
    //lines
    //of
    //code to set up and display menu
}
@end

We end up with shorter and less code.

Check out the developer docs for more details on how events are delivered

[1] The responder chain ends with the UIApplication delegate only when the delegate inherits from UIResponder. Recent versions of Xcode creates projects with the delegate inheriting from UIResponder. If you have an existing project that was created using an old version of Xcode, you will need to change it yourself.


Your feedback is valuable: Do you want more nuggets like this?   Yes   or   No

.

.

Like this and want such iOS dev nuggets to be emailed to you, weekly?

Sign Me Up! or follow @iosdevnuggets on Twitter

.

View archives of past issues