respondsToSelector: not working for delegate

17.7k views Asked by At

I am new to iOS world and have run into an issue while trying to pass a value from a TableView back to the home controller.

The scenario that I am working on is

  1. Home Controller has a button
  2. Click of button opens a list of items in the second UIViewController
  3. User selects an item from list
  4. Selected item is added to another list on Home Controller

Really appreciate any pointers for this issue:

This is how I am preparing for Segue on Home

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    UIViewController *destination = segue.destinationViewController;
    if ([destination respondsToSelector:@selector(setDelegate:)]) { 
        [destination setValue:self forKey:@"delegate"];
    }

}

SecondController has a delegate id, so I am assuming that delegate is set as "respondsToSelector" returns true for "setDelegate"

Now, in SecondController when user selects an item I call didSelectRowAtIndexPath & viewWillDisappear methods to set the item and make the view disappear:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    POSAllItemsCell *cell = (POSAllItemsCell *) [tableView cellForRowAtIndexPath:indexPath];

    item = [NSDictionary dictionaryWithObjectsAndKeys:cell.name, @"Name", cell.price, @"Price", cell.code, @"Code", nil];

    [self dismissViewControllerAnimated:YES completion:NULL];

}

- (void)viewWillDisappear:(BOOL)animated { 
    [super viewWillDisappear:animated]; 

    if ([delegate respondsToSelector:@selector(setSelection:)]) {
        [delegate setValue:item forKey:@"selection"];
    }
}

Now the problem here is that respondsToSelector for setSelection returns false even though I do have setSelection method in my HomeController:

- (void) setSelection:(NSDictionary *)dict {
    if (![dict isEqual:selection]) {
        ......
    }
}

Apologies in advance if my question in not clear or is not well formatted. BTW this is for iOS 5 with Xcode 4.2

2

There are 2 answers

6
iwasrobbed On BEST ANSWER

For delegation to work, you need to set it up like this:

In your FirstViewController.h header file, make sure you are declaring that the second view controller is conforming to the delegating view controller's protocol:

@interface FirstViewController : UIViewController <SecondViewControllerDelegate>

You can see that the delegate goes within the < > symbols in the header file of a view controller. If there are required delegate methods within that protocol, Xcode will show warnings if you don't define them in the implementation file.

Then you have your delegate method defined in the FirstViewController.m file:

- (void)setSelection:(NSDictionary *)dict {
    if (![dict isEqual:selection]) {
        ......
    }
}

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([[segue identifier] isEqualToString:@"someSequeIdentifierHere"]) {
        SecondViewController *sv = segue.destinationViewController;
        sv.delegate = self;
    }
}

You'll notice that instead of using UIViewController in the prepareForSegue method, you should just cast it to the actual view controller so you can set the properties on that. This way, you don't have to test if the view controller responds or not since it either has a delegate property defined or it doesn't (in which case, you need to add one).

To setup your original delegate protocol, you typically follow this format in your SecondViewController.h file:

@protocol SecondViewControllerDelegate <NSObject>
- (void)setSelection:(NSDictionary *)dict;
@optional
- (void)someOptionalDelegateMethodHere;
@end

@interface SecondViewController : UIViewController

@property (nonatomic, weak) id <SecondViewControllerDelegate>delegate;

@end

Delegates should almost always be weakly defined for ARC.

Then when you want to notify the delegate in SecondViewController.m

- (void)viewWillDisappear:(BOOL)animated { 
    [super viewWillDisappear:animated]; 

    if ([self.delegate respondsToSelector:@selector(setSelection:)]) {
        [self.delegate setSelection:item];
    }
}

Since delegate is defined as a public property in the .h file, you can reference it either as self.delegate or _delegate but referencing it as delegate makes me think you incorrectly defined it as a private instance variable instead.

About the only time this type of a pattern won't respond to respondsToSelector: is when you don't correctly assign that delegate to your FirstViewController

Hope this helps!

3
TotoroTotoro On

Try this:

if ([destination respondsToSelector:@selector(setDelegate:)]) { 
    [destination performSelector:@selector(setDelegate:) withObject:self];
}