-2

I've setup a pretty simple delegate, for a UIView. However, when I try to do:

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

My self.delegate is null. I checked the setter that the class being set as the delegate is right by:

- (void)setDelegate:(id<MyViewDelegate>)delegateClass
{
    // Whether I overwrite this method or not, it's still null.
    NSLog(@"%@", delegate);
    _delegate = delegateClass;
}

And it's correct. But by the time it's called in my IBAction - it's null.

EDIT: To clarify, I only dropped this in to see what was being passed in. If I don't overwrite this method, it's still null.

My code:

MyView.h

#import <UIKit/UIKit.h>

@class MyView;

@protocol MyViewDelegate <NSObject>
@optional
- (void) myDelegateMethod:(MyView *)sender;
@end

@interface MyView : UIView

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

- (IBAction)myButton:(id)sender;

@end

MyView.m

@implementation MyView

@synthesize delegate;

- (id)init
{
    if (!(self = [super init])) return nil;

    NSArray *subviewArray = [[NSBundle mainBundle] loadNibNamed:@"MyView"
                                                          owner:self
                                                        options:nil];

    return self;
}

- (IBAction)myButton:(id)sender
{
    // NOTE: Here, self.delegate is null
    if ([self.delegate respondsToSelector:@selector(myDelegateMethod:)]){
        [self.delegate myDelegateMethod:self];
    }
}

@end

MyCollectionViewCell.m

#import "MyCollectionViewCell.h"
#import "MyView.h"

@interface MyCollectionViewCell() <MyViewDelegate>
@property (nonatomic, strong) MyView *myView;
@end

@implementation MyCollectionViewCell

@synthesize myView;

- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];

    if (self){
        [self setup];
    }

    return self;
}

- (void)setup
{
    self.myView = [MyView new];
    self.myView.frame = CGRectMake(0,0,self.bounds.size.width, self.bounds.size.height);
    self.myView.alpha = 0.0;
    self.myView.layer.cornerRadius = 5.0f;
    self.myView.layer.masksToBounds = YES;
    self.myView.delegate = self;

    [self addSubview:self.myView];
}

// The delegate method
- (void)myDelegateMethod:(MyView *)sender
{
    NSLog(@"This is never called...");
}

@end
Matt
  • 1,473
  • 2
  • 11
  • 13
  • 1
    Is it possible that the delegate has been released? Try changing the delegate property from weak to assign and turn on zombies - see if you get an error. – Paulw11 Mar 14 '15 at 21:33
  • initWithFrame is called ? – Selvin Mar 14 '15 at 21:35
  • @Paulw11 Tried but no error and still null. Also tried setting it to strong (I know you usually shouldn't for delegate properties) but that didn't help either. – Matt Mar 14 '15 at 21:39
  • @Selvin Yep. The view is added correctly, no errors but then the delegate is nullified by the time I call the IBAction. – Matt Mar 14 '15 at 21:41
  • Try setting the ViewController as the delegate instead of collectioviewcell – Selvin Mar 14 '15 at 21:45
  • Show us the code where you set `delegate`. (I'm betting you're creating multiple instances of the class containing your delegate pointer.) – Hot Licks Mar 14 '15 at 22:00
  • @HotLicks That's it there. Inside the setup method I'm setting self.myView.delegate = self; which is pointing to the MyCollectionViewCell. I've just tried moving that out to the ViewController, as suggested by Selvin, but the same thing is happening. Setup is called once for each cell that is created. – Matt Mar 14 '15 at 22:08
  • And how many other places do you create a "MyView" object? – Hot Licks Mar 14 '15 at 22:11
  • self.myView = [MyView new]; may be the problem. Try using stackoverflow.com/a/15406012/569497 to allocate the view – Selvin Mar 14 '15 at 22:12
  • @HotLicks Just here, It's a specific view that belongs inside that cell, for only this ViewController. It should be pretty straight forward and I'm at a loss for ideas. Just doing what selvin has suggested changing [MyView new]; – Matt Mar 14 '15 at 22:17
  • @Matt have you solved this problem? – Selvin Mar 15 '15 at 08:09
  • @Selvin Yep. See comment on your answer below. Thanks for your help :) – Matt Mar 15 '15 at 12:39

2 Answers2

2

To MyCollectionViewCell add this:

- (void)dealloc
{
    NSLog(@"MyCollectionViewCell dealloc called")
}

delegate is a weak reference. So if the instance which is set to the delegate is released, it will become nil. The purpose of the above is to confirm this is happening.

Why would this happen? If nothing is maintaining a strong reference to the MyCollectionViewCell instance, then it will be released.

Ideally, you'd also want to add an NSLog to myButtonPressed:. It would also be beneficial if you NSLog'd the setting of the delegate. So your output could be potentially something like:

Delegate set
MyCollectionViewCell dealloc called <- this would indicate that the delegate becomes nil based on the instance being released
myButtonPressed: hit

This is a working theory, given the amount of info you have provided.

Mobile Ben
  • 7,121
  • 1
  • 27
  • 43
  • Thanks for the suggestion. I just tried it but the collectionviewcell isn't being deallocated. The code that's setting up the cell isn't anything funky either. It's just the usual dequeueReusableCellWithReuseIdentifier – Matt Mar 14 '15 at 21:48
2

Set the viewcontroller as the delegate of MyView object instead of collection view cell.

In your view controller, inside the datasource method

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView 
                  cellForItemAtIndexPath:(NSIndexPath *)indexPath

do something like this.

cell.myView.delegate = self;

Update

As observed, the problem is in allocating the UIView subclass. Initializing the view as

self.myView = [MyView new];

does not work for some reason. To properly allocate the view, the following approach can be used.

 self.myView = [[[NSBundle mainBundle]
        loadNibNamed:@"MyView"
        owner:self options:nil]
         firstObject];
Community
  • 1
  • 1
Selvin
  • 12,333
  • 17
  • 59
  • 80
  • Okay, tried it but getting the same thing. It's null. When I overwrite the setter it's saying is the one being set as the delegate. But when I run the app and click the button to trigger the IBAction self.delegate is still (null) – Matt Mar 14 '15 at 22:05
  • `MyView` init method can be wrong. It could create 2 instances – Selvin Mar 14 '15 at 22:07
  • http://stackoverflow.com/a/15406012/569497 use this approach to init your subclass – Selvin Mar 14 '15 at 22:10
  • Thanks, I'll change that. I'm guessing when you initialize it that way initWithCoder is called? – Matt Mar 14 '15 at 22:16
  • This fixed it. Changing [MyView new] to loading the nib as suggested at that link fixed it. I'm not sure entirely why at the moment. init doesn't appear to be called anymore times than initWithCoder (with the other initializing method) but my method is definitely the culprit. I've been using [MyView new] for a while as a shortcut to alloc init - should I be staying away from this? Or at least when initializing a view with a nib? If you wouldn't mind updating your answer with your other suggestion, I'll accept it. Thank you very much for your help Selvin. – Matt Mar 15 '15 at 12:38
  • 1
    For allocating custom UIView subclasses I have always used `[[[NSBundle mainBundle] loadNibNamed:@"nibname" owner:self options:nil] firstObject];` method. For `UIViewController` subclasses, [ViewController new] works. But not for `UIView` subclasses. Very strange indeed! – Selvin Mar 15 '15 at 13:55