8

I'm having trouble understanding how this works. I've read many threads on SO about it - such as UITableView dequeueReusableCellWithIdentifier Theory and How does dequeueReusableCellWithIdentifier: work?.

However, my UITableView succesfully dequeues a cell each time (it's never nil), even when it first loads. I was under the impression that similar cells should use the same identifier, so you only have to change what's necessary.

Because

if (!cell) {
    NSLog(@"New cell");
    cell = [[UITableViewCell alloc] initWithStyle:someStyle reuseIdentifier:someIdentifier];
}

Never gets called, I'm not sure how I'm supposed to handle cells in the same table with different styles, because the style can only be set in the initializer.

I also tried using different cell identifiers, to make sure it wasn't reusing cells from a different table or something. I am registering these identifiers with [tableView registerClass: forCellReuseIdentifier:]

If I understand, this method should only return previously created cells that have been moved off the screen (hidden, i.e. can be reused). Then how come it returns a cell the first time it's called?


Edit: So the confusion was using [tableView dequeueReusableCellWithIdentifier: forIndexPath:] instead of [tableView dequeueReusableCellWithIdentifier:] (the first requires registering the identifier, the second will return nil if none is available - the behavior I was expecting above).

However, I noticed that when I changed my code to use [tableView dequeueReusableCellWithIdentifier:], it creates a new cell, and its contentView.frame has a width of 320 (full width). Before, when I did dequeue...forIndexPath it would give a width of 302, or the visual/"real" width of the cell. Why is this?

Also, is there a way to specify the style of the UITableViewCells regstiered for reuse?


Solution: So I found this thread UITableView cell.contentView.bounds.size.width Changes With Cell Reuse, which says when you set the autoresizingmask to UIViewAutoresizingFlexibleLeftMargin, it's fixed when you try to do relative positioning (the contentView width is initially the fully width, but when you present it it's shrunk, so if you do your calculations right it'll still show up properly).

I was positioning a UISwitch on the right - and when I set the autoresizing mask it works when it's first displayed but shifted over another ~20 pixels when I switched it. I don't know what caused that extra shift, but I ended up solving it by simply setting the UISwitch as the cell's accessoryView.

(This is partially off topic from the original question, but if someone stumbles on this maybe it'd be useful). For anyone wondering specifically about the original question, the answer is under the first edit.

Community
  • 1
  • 1
Raekye
  • 5,081
  • 8
  • 49
  • 74
  • 1
    Are your cells created into a UITableViewController from a storyboard ? – danypata Jun 18 '13 at 19:17
  • All created programmatically... yeah, a lot of people say use the storyboard... but I haven't gotten used to it and also I feel I'll understand it better by knowing what's actually understand – Raekye Jun 18 '13 at 19:40
  • 1
    No problem with that, but I was wondering because when you are creating the cells into a UITableViewContoller from a storyboard the `deque` method will always return a valid cell (if proper identifier is used) – danypata Jun 18 '13 at 19:47

4 Answers4

9

When you call [tableView registerClass: forCellReuseIdentifier:], you're teaching the table view what to do when you later use the specified ReuseIdentifier. So, when you later call [tableView dequeueReusableCellWithIdentifier:] it will either:

A. Get a cell that has previously been created and isn't currently being used

OR

B. Create a new cell of the class you specified

So, when you dequeue, you will always get an instance. If you want to create new cell instances yourself with initWithStyle:reuseIdentifier: then you shouldn't register a class with the table view. Alternatively, leave the registration and add logic to specify everything that needs to be configured (and consider using multiple different cell classes and reuse identifiers).

Wain
  • 118,658
  • 15
  • 128
  • 151
  • 1
    If I take out the `[tableView registerClass: forCellReuseIdentifier:]`, I get a "NSInteralInconsistencyException: unable to dequeue a cell with identifier [my identifier]". And when I do the registration, it always returns a cell, even if I'm using different identifiers, so I can't create different cell styles – Raekye Jun 18 '13 at 19:39
  • You must be using `dequeueReusableCellWithIdentifier:forIndexPath:` then - it requires that you register a class or NIB. You might want to show the code for your registrations and `tableView:cellForRowAtIndexPath:` if you want more help. – Wain Jun 18 '13 at 19:47
  • Yeah, I just noticed that in another question. So using the method without an index path works as expected, but there's another problem now (updating questino) – Raekye Jun 18 '13 at 19:49
0

because the first time the cell is nil that is why this gets called:

if (!cell) {
NSLog(@"New cell");
cell = [[UITableViewCell alloc] initWithStyle:someStyle reuseIdentifier:someIdentifier];
}

but then if the cell is already ready for reuse and basically its not nil - it returns the cell and it does not hit the above if statement

Jatin
  • 1,668
  • 2
  • 16
  • 23
0

From the apple docs at https://developer.apple.com/library/ios/#documentation/UIKit/Reference/UITableView_Class/Reference/Reference.html#//apple_ref/doc/uid/TP40006943

Call this method from your data source object when asked to provide a new cell for the table view. This method dequeues an existing cell if one is available or creates a new one using the class or nib file you previously registered. If no cell is available for reuse and you did not register a class or nib file, this method returns nil.

The dequeue method will

  1. Return a recycled cell if one is available

  2. Create a new cell if you registered one (you mentioned you did this)

  3. If none of these are true, it returns nil

I'm guessing if you remove the registration (which may be hidden in a xib) then you will get the nil result.

Brian Broom
  • 497
  • 4
  • 11
  • I am not using the intercae builder. The whole point of my question was that it never returns nil. In terms of "return a recycled cell", is it possible to have a recycled cell when the applicatino first starts up? – Raekye Jun 18 '13 at 19:34
  • The first cell is not recycled, it's just been automatically created by the table view. – Wain Jun 18 '13 at 19:48
  • Yeah thanks, I overlooked the difference between the version with `forIndexPath:` and the one without – Raekye Jun 18 '13 at 19:56
-1

if you see UITableView.h

Beginning in iOS 6, clients can register a nib or class for each cell.

If all reuse identifiers are registered, use the newer -dequeueReusableCellWithIdentifier:forIndexPath: to guarantee that a cell instance is returned.

Instances returned from the new dequeue method will also be properly sized when they are returned.

(void)registerNib:(UINib *)nib forCellReuseIdentifier:(NSString *)identifier NS_AVAILABLE_IOS(5_0);

(void)registerClass:(Class)cellClass forCellReuseIdentifier:(NSString *)identifier NS_AVAILABLE_IOS(6_0);
AlvaroAV
  • 10,335
  • 12
  • 60
  • 91