ref – http://stackoverflow.com/questions/3061401/respondstoselector-not-found-in-protocols
Description
We are going to create a custom UIPickerView. This Picker View get the string of your selection, then send the delegating class the result. Hence a UIViewController can create this custom class, conform to the protocol, connect the delegate, and start receiving strings of the selected entries in the picker view.
Create class and override
1 2 3 4 |
@interface RTPickerView : UIPickerView {} @end |
Implement the protocol, create the delegate
For our case, we are going to create a single delegate which forces the UIViewController to implement our single delegate’s methods.
All default UIPickerViewDelegate and UIPickerViewDataSource will delegate to our custom RTPickerView.
If you want to group your custom delegate methods along with UIPickerViewDelegate, UIPickerViewDataSource, under RTPickerDelegate, you can declare it like so:
1 2 |
@protocol RTPickerDelegate <UIPickerViewDelegate, UIPickerViewDataSource> @end |
In your ViewController, when you conform to RTPickerDelegate
and you go self.pickerView.delegate = self,
Then you will be receiving all messages from all delegates.
However, for the sake of demonstration, we are going to create a custom protocol. When the ViewController uses it, it can receive 2 delegates. One is ‘delegate’ from UIPickerViewDataSource and UIPickerViewDelegate. One is RTPicker_Delegate, which is for taking care of our custom protocol methods.
That way, you can pick and choose which protocol you will want to conform to.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
//make sure conform to NSObject by default so we can use respondsToSelector @protocol RTPickerDelegate <NSObject> //important. Requires ViewControllers to implement this method if they conform //to RTPickerDelegate @required - (void)RTPicker:(UIPickerView *)pickerView didSelectData:(NSString *)name; @optional @end @interface RTPickerView : UIPickerView { } //to set by viewcontroller @property (nonatomic, weak) id <RTPickerDelegate> RTPicker_Delegate; @end |
The RTPicker_Delegate delegate that we want our calling UIViewController to access should be weak. It should not be strong because as an object, we don’t want the controlling UIViewController to keep us in memory.
Implementation
Our Custom object conforms to View Delegates and Data Source delegates. Thus, we
take care of these protocol in this class. The calling ViewController is welcome to access the delegate property, and then implement these protocol methods too if it wants.
1 2 3 4 |
@interface RTPickerView () <UIPickerViewDelegate, UIPickerViewDataSource> { } @end |
UIPickerViewDataSource methods
1 2 3 4 5 6 7 8 9 10 11 12 13 |
@implementation RTPickerView @synthesize RTPicker_Delegate; // returns the number of 'columns' to display. - (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView { return 1; } // returns the # of rows in each component.. - (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component { return [self.dataArray count]; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
-(instancetype)initWithFrame:(CGRect)frame { NSLog(@"%s", __FUNCTION__); if(self = [super initWithFrame:frame]) { self.dataArray = [NSArray arrayWithObjects:@"gary",@"dean",@"ricky", nil]; //the view and data source delegates come here super.delegate = self; super.dataSource = self; [self setBackgroundColor:[UIColor redColor]]; return self; } return nil; } |
UIPickerViewDelegate protocol methods
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
- (UIView *)pickerView:(__unused UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(__unused NSInteger)component reusingView:(UIView *)view { if (!view) { view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 280, 30)]; UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(35, 3, 245, 24)]; label.backgroundColor = [UIColor clearColor]; label.tag = 1; label.text = [self.dataArray objectAtIndex:row]; [view addSubview:label]; UIImageView *flagView = [[UIImageView alloc] initWithFrame:CGRectMake(3, 3, 24, 24)]; flagView.contentMode = UIViewContentModeScaleAspectFit; flagView.tag = 2; [view addSubview:flagView]; [view setBackgroundColor:[UIColor clearColor]]; } return view; } - (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component { return [self.dataArray objectAtIndex:row]; } - (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component { NSLog(@"selected: %lu %lu", (long)row, (long)component); NSLog(@"%@", self.delegate); NSString * value = [self pickerView:pickerView titleForRow:[pickerView selectedRowInComponent:0] forComponent:0]; NSLog(@"%@", value); [self pickerView:pickerView didSelectData:value]; } |
RTPickerDelegate protocol method
When we are processing didSelectData, we don’t want the calling UIViewController to suddenly disappear on us. Thus, we strong ViewController temporarily. The local stack has a strong pointer on it, it makes the delegated UIViewController process messages, and this method will finish. Our strongDelegate is declared on the stack, and thus, will pop. Free-ing this class from the UIViewController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
- (void)pickerView:(UIPickerView *)picker didSelectData:(NSString *)name { NSLog(@"%s - %@", __FUNCTION__, name); //self delegate is weak, so when we are processing, let's use a strong __strong id <RTPickerDelegate> strongDelegate = RTPicker_Delegate; if([strongDelegate respondsToSelector:@selector(pickerView:didSelectData:)]) { //however is ownder of our delegate has to process it! [strongDelegate pickerView:picker didSelectData:name]; } } |
How to Use
Conform to the protocol
1 2 3 4 5 6 7 |
#import "ViewController.h" #import "RTPickerView.h" @interface ViewController () <RTPickerDelegate> { } |
Create the object, make sure you add it to the UIViewController, and then connect the delegate. After connecting the delegate, all messages from that particular action which triggers the protocol method, will be sent to the ViewController for answers. You, as the developer of the UIViewController will need to implement it.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. self.customPicker = [[RTPickerView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 200.0f)]; //the picker delegate comes here, to get the name self.customPicker.RTPicker_Delegate = self; [self.view addSubview: self.customPicker]; [self.view setBackgroundColor:[UIColor orangeColor]]; } |
Implementation of the protocol method
1 2 3 |
- (void)pickerView:(UIPickerView *)pickerView didSelectData:(NSString *)name { NSLog(@"%s - You've selected: %@", __FUNCTION__, name); } |