part of the groupelephant.com family
beyond corporate purpose

Custom delegates, the Apple way

03 Jan 2011

When you get into Mac or iOS development, you’ll soon be confronted with delegates. And after a certain period of time when you gain more experience, you’ll start creating your own custom delegate calls.

How Apple uses delegates

Let’s say we want to log the number of characters entered in a UITextView. We’ll have to assign a delegate to the UITextView instance and then implement the textViewDidChange method.

UITextView *textView = [[UITextView alloc] init];
textView.frame = CGRectMake(0, 0, 100, 30);
textView.delegate = self;
[self.view addSubview:textView];

This code snippet creates the UITextView somewhere in the viewDidLoad method from out main UIViewController. Next up: implement the textViewDidChange method.

- (void)textViewDidChange:(UITextView *)textView {
  NSLog(@"number of chars: %i", [textView.text length]);
}

This will log the number of characters entered every time you change something in the UITextView.

But there is a warning

The code above will work perfectly but there will be a warning.

Delegate warning

The warning thrown is this one: class ‘ExampleViewController’ does not implement the ‘UITextViewDelegate’ protocol. It means we have to follow the UITextViewDelegate protocol and set it in the header file like this:

The solution to remove the warning is fairly easy, just add the UITextViewDelegate protocol to the header file.

@interface ExampleViewController :
                   UIViewController <UITextViewDelegate>

To make a class conform to a protocol, you just have to add it in between the < and >.

Create your custom delegate call

Now what if you want a custom view class to call a method in your controller because you think the code functionality belongs to the controller. You’ll first create a property where you can assign the delegate:

@property (nonatomic, retain) NSObject *customDelegate;

Your delegate instance is now available in your class, so when you call the setColor: method on your instance, than you want to call the delegate’s changeColor: method if available in the customDelegate: instance.

You can find more info on the difference between id and NSObject on this blog. There reason I’m using NSObject is because it doesn’t generate warnings when using the respondsToSelector:.

In the code below we’ll first check if the method is available, and if so we call it and pass the the current color so the controller can do the rest.

- (void)setColor:(UIColor *)color {
  SEL method = @selector(changeColor:);
  if ([customDelegate respondsToSelector:method]) {
    [customDelegate performSelector:method
                    withObject:color];
  }
}

For those that don’t really know what selectors are, they can read it here

But I want a warning!

It can come in handy to throw a warning to notify the developer that he can or has to implement certain methods in order for the delegate to work properly.

You should first create a protocol that defines the @required and @optional methods that the delegate class can or has to implement. And you can do this in the header file (or in another file).

@protocol ExampleViewDelegate
@required 
- (void)changeColor:(UIColor *)color;
@optional
- (void)clearColor;
@end

The methods below the @required directive are all obligatory. The ones below @optional are not. Now this won’t throw any warning it’s just a preparation for what is coming.

Now you have to do a small modification to the @property where the customDelegate is defined. We have to tell the customDelegate that it should implement the ExampleViewDelegate protocol.

@property (nonatomic, retain)
                  id<ExampleViewDelegate> customDelegate;

When we now assign the delegate, a warning is thrown when the protocol is not implemented (cfr. How Apple uses delegates). So we just have to implement the protocol to stop the warning from appearing.

@interface ExampleViewController
                 : UIViewController <ExampleViewDelegate>

And when we don’t implement the changeColor: method, then another warning will arise, because this method is set to required in the protocol.

Now let’s go and delegate!

Reference

You can find a small project with the UITextView and the custom delegate examples on Github

blog comments powered by Disqus