Search code examples
iosuibuttonactiontargets

UIButton with target:self not being sent to self?


I'm creating a new UIViewController (call it MyViewController), and adding the view (MyView) as a subview of a TableViewCell. Within MyView , there's a button. That button is created programmatically during MyViewController's init, as such:

-(id)initWithFrame:(CGRect)frame {
    self = [super init];
    if(self) {
        [self.view setFrame:frame];

        _yesButton = [[UIButton alloc] initWithFrame:CGRectMake(frame.size.width-150, 40, 140, 30)];
        [_yesButton setTitle:@"Yeah!" forState:UIControlStateNormal];
        [_yesButton addTarget:self action:@selector(didClick) forControlEvents:UIControlEventTouchUpInside];
        [self.view addSubview:_yesButton];
    }
    return self;
}

Now, seems straightforward. It displays properly, everything looks great in the simulator.

But when I click on the "_yesButton" within MyView, I get a crash with this error:

-[_UITableViewCellSeparatorView didClick]: unrecognized selector sent to instance 0x7b7f7ec0

What? When did "_UITableViewCellSeparatorView" come into the equation? I specifically told the _yesButton to set the Target to "self", so the selector should be sent to MyViewController, right? I could even imagine it getting tripped up and sending it to the UITableViewCell, since MyView is embedded within a TableViewCell, but why a SeperatorView?

Can anyone tell me how to get my _yesButton to send the call back to the MyViewController that it's being created within? And for bonus points, can you explain how "_UITableViewCellSeparatorView" became a thing in this conversation at all?

Edit: Here's how I'm building the cell in the TableView, and adding MyView to it. Note that I'm deliberately not using dequeuing for this row, although that might change if it's the source of the problem.

UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"MyViewCell"];

MyViewController *myViewController = [[MyViewController alloc] initWithFrame:cell.contentView.frame];
[cell.contentView addSubview:myViewController.view];

return cell;

And the didClick method is currently empty, (it never even gets there, so I haven't gotten that far in writing it), but it's currently defined within MyViewController simply as:

-(void)didClick {

}

Solution

  • Solution #1

    Actually this is because you MyViewController is not retain by ARC. The dealloc method id being called. Add ans instance of your controller in your UITableViewController will fix the issue and make ARC retains your controller.

    Solution #2

    Try something like this.

    Create a custom UITableViewCell :

    MyCustomCell.h :

    #import <UIKit/UIKit.h>
    
    @interface MyCustomCell : UITableViewCell
    
    @property (strong, nonatomic) UIButton *yesButton;
    
    -(id)initWithFrame:(CGRect)frame;
    
    @end
    

    MyCustomCell.m :

    #import "MyCustomCell.h"
    
    @implementation MyCustomCell
    
    - (void)setSelected:(BOOL)selected animated:(BOOL)animated {
        [super setSelected:selected animated:animated];
    
        // Configure the view for the selected state
    }
    
    -(id)initWithFrame:(CGRect)frame {
        self = [super init];
        if(self) {
           [self.view setFrame:frame];
    
           _yesButton = [[UIButton alloc] initWithFrame:CGRectMake(frame)];
           [_yesButton setTitle:@"Yeah!" forState:UIControlStateNormal];
           [self.contentView addSubview:_yesButton];
        }
        return self;
    }
    @end
    

    In your UITableViewControllerin the viewDidLoad function :

    [self.tableView registerClass:[MyCustomCell class] forCellReuseIdentifier:CellIdentifier];
    

    Then in your tableView:cellForRowAtIndexPath:

    MyCustomCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifierr forIndexPath:indexPath];
    if (cell == nil) {
         cell = [[MyCustomCell alloc] initWithFrame:frame];
         [cell.yesButton addTarget:self action:@selector(didClick) forControlEvents:UIControlEventTouchUpInside];
    }
    

    And now implement the following in your UITableViewController:

    -(void)didClick {
       // The following identify the in which cell the action has been triggered
       NSSet *touches = [event allTouches];
       UITouch *touch = [touches anyObject];
       CGPoint currentTouchPosition = [touch locationInView:self.tableView];
       NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint: currentTouchPosition];
       if (indexPath != nil)
       {
        // Do whatever you want for the given cell
       }
    }