2

I am using registerNib:forCellReuseIdentifier: to load cell from nib b/c I was told that with that I can always get a cell from [tableView dequeueReusableCellWithIdentifier:] and thus reduce those boilerplate codes.

I do always get a cell but the problem is that my IBAction (a button in my cell) started to fail by raising the exception 'NSInvalidArgumentException', reason: xxx unrecognized selector sent to instance.

If I remove the call registerNib:forCellReuseIdentifier: and add these codes as usual (below), everything works fine. So I guess the problem was caused by this call.

So what did I do wrong?

BTW, I set the file owner of my cell nib file to my table view controller. "Programming IOS 5" said "There is no need to specify a File’s Owner class in the nib" in this case, but since I need to set my IBAction I still set it. I don't think this will cause the problem, right ?

//The "old" codes without calling registerNib:forCellReuseIdentifier:
UITableViewCell *cell =[tableView dequeueReusableCellWithIdentifier:@"MyCell"];
if (cell == nil) {
    NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"MyCell"
                                                 owner:self 
                                               options:nil];
    cell = [nib objectAtIndex:0];

...

Qiulang
  • 10,295
  • 11
  • 80
  • 129

3 Answers3

1

Your IBAction is in the controller that loads the cell, I take it? Don't think that's going to work. If you look at the call stack when dequeing -- as I'm doing for a spot of debugging right now -- you'll see

- [YOURCELLCLASS awakeFromNib];
- [UINib instantiateWithOwner:options:]
- [UITableView dequeueReusableCellWithIdentifier:]

How does the table view know what controller object it should be passing to UINib as the owner to match the class you declared it as? Doesn't appear there's any way for it to do that. And indeed, when I replicate your button above and check its target in -awakeFromNib, that target is very definitely not the controller; thus your crash. Interestingly, it doesn't appear to be the table view either, which is what I would have expected; it's an NSObject whose address doesn't match anything obvious at first glance.

shrug Any-ways, the lesson appears to be that you shouldn't use the file's owner for anything in this xib. Move your logic into a cell custom class, or set the control(s)' targets yourself after dequeing.

Alex Curylo
  • 4,744
  • 1
  • 27
  • 37
0

I hit the problem again and did some further investigation. And here is what I found,

  1. Setting File's own in the cell nib is pointless(and dangerous). I set file's own in xib to hook up my IBAction to MyCell class, but then I realized that the owner provided in [[NSBundle mainBundle] loadNibNamed:owner:options:] is the one runtime honored. It is easy to verify that, just provide 2 IBActions (same method name), one in MyCell and the other in owner and watch which one was called.
  2. There are many articles said you can create cell by calling cell = [[MyCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]; But I think this was wrong. To use the cell in xib file you have to call [[NSBundle mainBundle] loadNibNamed:owner:options:]
  3. Now I am kinda believe this is a bug that using registerNib:forCellReuseIdentifier: will set your IBAction to the wrong target or you just can not use it with IBAction together. Like @alexcurylo said when you used it there is no way to set file's owner correctly (even you had set it in xib). The answer to the question here UITableView registerNib:forCellReuseIdentifier: said runtime just set the owner to nil. But the weird thing is that IBOutlet is always set correctly.
  4. So if you just need to update IBOutlet's property, e.g. update UILabel's text, using registerNib:forCellReuseIdentifier: is simpler b/c you always get a cell, no need to check it again nil. But if you need action, you have to call addTarget:action: after you get your UIView(with either [[NSBundle mainBundle] loadNibNamed:owner:options:] or registerNib:forCellReuseIdentifier:)
  5. To create a customized table cell you can either subclass UITableViewCell and place controls in codes or just use cell xib file, but no need to do both(which was also suggested by many articles). Because you won't get your IBAction right and you can easily get your subview without IBOutlet. It is probably simpler to just use xib. When you use xib file, remember to set the tag of your subview in cell then you can get UIViews with [cell viewWithTag:]
Community
  • 1
  • 1
Qiulang
  • 10,295
  • 11
  • 80
  • 129
0

I think there are two main options if you'd like to use IB to connect your actions:

  1. Don't use registerNib:forCellReuseIdentifier: - load the nib manually in your cellForRowAtIndexPath: using the old-style code.
  2. Send the IBActions to First Responder instead of Files Owner, in flagrant violation of Guy English's advice.
Sam
  • 4,694
  • 2
  • 36
  • 47