views:

275

answers:

2

I recently began trying my hand at using protocols in my Objective-C development as an (obvious) means of delegating tasks more appropriately among my classes. I completely understand the basic notion of protocols and how they work. However, I came across a roadblock when trying to create a custom protocol that in turn implements another protocol. I since discovered the solution, but I am curious why the following DOES NOT work:

@protocol STPickerViewDelegate < UIPickerViewDelegate >

- ( void )customCallback;

@end

@interface STPickerView : UIPickerView
{
    id < STPickerViewDelegate > delegate;
}

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

@end

Then in a view controller, which conforms to STPickerViewDelegate:

STPickerView * pickerView = [ [ STPickerView alloc ] init ];
pickerView.delegate = self;

- ( void )customCallback
{
    ...
}

- ( NSString * )pickerView:( UIPickerView * )pickerView titleForRow:( NSInteger )row forComponent:( NSInteger )component
{
    ...
}

The problem was that pickerView:titleForRow:forComponent: was never being called. On the other hand, customCallback was being called just fine, which isn't too surprising. I don't understand why STPickerViewDelegate, which itself conforms to UIPickerViewDelegate, does not notify my view controller when events from UIPickerViewDelegate are supposed to occur. Per my understanding of Apple's documentation, if a protocol (A) itself conforms to another protocol (B), then a class (C) that conforms to the first protocol (A) must also conform to the second protocol (B), which is exactly the behavior I want and expected.

What I ended up doing was removing the id< STPickerViewDelegate > delegate property from STViewPicker and instead doing something like the following in my STViewPicker implementation where I want to evoke customCallback:

if ( [ self.delegate respondsToSelector:@selector( customCallback ) ] )
{
    [ self.delegate performSelector:@selector( customCallback ) ];
}

This works just fine, but I really am puzzled as to why my original approach did not work.

+2  A: 

The problem was that UIPickerView already has a delegate member variable, and you were declaring another one in a subclass which was what was being set, while the superclass's delegate variable remained nil and therefore any delegate methods would not be called on the class you expect it to be called on. In this case what you did is pretty much the only way to do it; if you need to extend the given protocol do so, have a class implement that, then just set the class as the UIPickerView's delegate.

Edit: btw, awesome avatar :)

Kevlar
The answers are SO obvious once someone points them out, eh? :) I guess my brain is just really tired. Yeah, we MOTHER lovers gotta stick together. :D
LucasTizma
A: 

I tried to do something similar - I assume you want to group extra methods you add to the UIImagePickerControllerDelegate in one file? I was running a UIImagePicker from two places and wanted it to behave the same way without duplicating code.

What I did was to add a category to the UIViewController, like this (below.) I'm fairly new to Objective-C (have used C++ for many years) so assuredly this probably violates the spirit of how you are "supposed" to do things (extending the protocol makes more sense), but my approach accomplished what I wanted, so I thought I'd toss it out.

UIViewController+imagePickerDelegate.h:

 @interface UIViewController (ImagePickerDelegate) <UINavigationControllerDelegate, UIImagePickerControllerDelegate>

    -(void)configurePicker:(UIImagePickerController*)picker;
    ...

@end

UIViewController+imagePickerDelegate.m:

   #import "UIViewController+imagePickerDelegate.h"

    @implementation UIViewController (ImagePickerDelegate) 

    -(void)configurePicker:(UIImagePickerController*)picker
    {
        picker.delegate = self;
        picker.allowsEditing = YES;
    }

    ....

@end
gulchrider