UITableViewDataSource method - objective-c

Here is the CellForRowAtIndexPath method of UITableViewDataSource protocol. I saw that code on a website.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *TableIdentifier = #"SimpleTableItem";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:TableIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:TableIdentifier];
}
cell.textLabel.text = [playersReady objectAtIndex:indexPath.row];
return cell;
}
My questions are:
Why when here defined cell wrote = [tableView dequeueReusableCellWithIdentifier:TableIdentifier]; ? What that means? If I commented that code everything going OK. For what that code? Hmmm...
How cell in if statement can be equals to nil if cell equals to TableIdentifier (SimpleTableItem)? For what wrote that code?
Why TableIdentifier equals to SimpleTableItem? For What?

An iPhone doesn't have a lot of memory. But even on modern computers you wouldn't want to initialize a new cell for every cell in your table. That's just wasting memory. So instead Apple came up with this idea of reusable cells. You only have to initialize a few cells that fill your screen (table view). Then, when the user scrolls down some new cells will appear at the bottom of the screen, but at the same time other cells will disappear at the top of the screen. So you can simply take those cells and reuse them.
Luckily UITableView manages this for you. All you have to do when you need to setup a new cell in that method is ask the table view, if it has any cells available that can be reused. If there are reusable cells, dequeueReusableCellWithIdentifier: will return one of them. But if there are non available yet (typically when you first fill up your table view with the initial cells) it will return nil. So you have to test if cell is nil and create a new cell from scratch if that's the case.
On iOS 6.0 there is a new method dequeueReusableCellWithIdentifier:forIndexPath: that always returns a valid cell (it creates the cell for you if there is no reusable cell yet).

Table view Create only those cell which can Display at one time on screen.After this system reuse cell for save memory .
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 20;
}
-(UITableViewCell *)tableView:(UITableView *)tableViewL cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableViewL dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
NSLog(#"Cell == nil so create a new cell....");
}else {
NSLog(#"Reuse Cell ");
}
return cell;
}
CellIdentifier use for identify cell for example if you add label on first ten table on 12 cell you add a button it give you a problem when you reuse cell.so that we need create a different cell For add button on cell and give it a Identifier string.

Related

UITableViewController odd behavior

Sorry if this question look stupid but after hours of digging and searching with no success, I need to ask it here for you help.
I have a UITableViewController with 15 custom cell. Each cell has a UITextField inside.
here is the part of code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
Static NSString *CellIdentifier = #"ADDFIELDCELL";
AddListContentCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[AddListContentCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
[cell.myTextField setTag:[self.tagsArray objectAtIndex:indexPath.row] integerValue]];
return cell;
}
The problem is when I type something in first cell (indexPath:0) after scroll down same text appear on 13th cell also. same mapping happens with 2nd and 14th cell.
I am really out of temper and do not what should I do. Any help will be appreciated.
The cell is being reused (hence the reuse identifier).
The textfield text for each cell needs to be linked to the datasource somehow (seems to be your tags array).
When you change the text on that cell, you have to modify the datasource text.
This way when the cell at index 2 rolls around, it will fill the textfield with whatever your tag item at index 2 of your datasource array has.

How to dynamically change a UITableViewCell's tag when cells in rows before it get removed?

UPDATE: I have solved this problem by retagging remaining cells in order by cycling through them in a for loop incrementing the indexpath's row every time up to the amount of data left.
I have a UITableView with 2 sections. One section is fixed. the other section changes dynamically, a user clicks a button and a new row is added. The cells contain three text boxes which the user can edit (tagged 1,2, and 3). The rows in this section are editable and a user can swipe and delete. All of this functionality is currently working fine. Here is the UI:
What isn't working fine is when I am trying to update the data source. I use an array that contain objects of a custom class called "ReceiptItem". When a user edits one of the UITextFields in a cell, I am updating the corresponding property in "ReceiptItem". I am currently using tags to track which cell and text field is being edited.
The code where I identify what cell and textfield are being edited is:
- (void)textFieldDidEndEditing:(UITextField *)textField{
//find the cell and textfield that just ended editing
int row = textField.superview.tag;
int column = textField.tag;
ReceiptItem *itemToBeUpdated = [[ReceiptItem alloc]init];
itemToBeUpdated = [receiptItemsArray objectAtIndex:row];
//update the receiptItem
switch (column) {
case 1:
//quantity text field
itemToBeUpdated.quantityValue = [textField.text doubleValue];
break;
case 2:
//item text field
if ([textField text].length == 0) {
itemToBeUpdated.itemName = #"Blank";
}
else{
itemToBeUpdated.itemName = [textField text];
}
break;
case 3:
//price text field
itemToBeUpdated.priceValue = [textField.text doubleValue];
break;
}
[receiptItemsArray replaceObjectAtIndex:row withObject:itemToBeUpdated];
}
Where I run in to problems is when a row gets deleted. I delete the corresponding object from my datasource and my array's count goes down by one. If I then try to edit a textfield in a cell further down from the cell that was deleted I get an "outofbounds" error because the tag for the cell is now greater than array's count. It is happening in this line (from above):
itemToBeUpdated = [receiptItemsArray objectAtIndex:row];
I am trying to figure out how I can either a)better track the cells, b) retag the cells when one is deleted or c) something else. Any answers? I can post the code where I cell is deleted if needed.
I guess your are taging the textField in cellForRowAtIndexPath's methods if (cell == nil) like,
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]
yourTextField.tag = indexPath.row;
}
}
Make the taging outside the if block so that when you delete a row and at reload data call your textField will get new tag like this :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]
}
yourTextField.tag = indexPath.row;
return cell;
}

dequeueReusableCellWithIdentifier always returns nil (not using storyboard)

I am creating the cell programatically using the reuse identifier.
Note - I am not using storyboard for creating the cell
Whenever the cell is dequeued, the cell is nil, so the cell needs to be newly created using alloc, which is expensive.
EDIT (added 1 more question and corrected code)
Question
Why does this dequeue always return nil ? How can I correct it ?
Does dequeue work only when used along with storyboard / nib file ?
Code
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(!cell) //Every time cell is nil, dequeue not working
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
return cell;
}
You need to first set the CellIdentifier as Cell. Are you doing that? When you are creating a new cell you need to assign this identifier Cell to it. only then iOS will be able to dequeueReusableCellWithIdentifier with that identifier. Programatically you can do it like so -
UITableViewCell *cell = [[UItableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:#"Cell"];
You can set identifier from Interface Builder too -
I was making a couple of mistakes:
I was using a subclass of UITableViewController, but was creating the tableView outside of the subclass
There is a tableView created in the table view controller, which is self.tableView In the tableview controller while returning the cell for index path, I was using self.tableView instead of tableView.
Also, ensure that the cell identifier is declared as static
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
Since tableView and self.tableView were representing different tables, the cell was not being dequeued from the same table and hence was always nil
This code should be generating the warning "control reaches end of non-void function" because you aren't actually returning anything. Add return cell; to the end of the function. Additionally, you never add the reuse identifier to the newly created cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
return cell;
}
First declare cell identifier for a tableViewCell at the viewDidLoad method as:
[tableView registerClass:UITableViewCell.class forCellReuseIdentifier:#"MyCell"];
Now recall the instance of the UITableViewCell with the same identifier "MyCell" as:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"MyCell" forIndexPath:indexPath];
Further just fill up the cell .. Now logic executes that limited number of cells are able to show enormously large list efficiently (using dequeue concept).
But remember to assign value (even nil if required) to every UIView used in the cell, otherwise overwriting / overlapping of text / images will happen.

Cells become empty after scrolling. (Xcode)

I have a problem with my tableview. When I scroll and a cell disappears from the screen it becomes blank. I have built a prototype cell in the storyboard with two labels and one imageview, it have the same identifier that I use in my code. I also have built a custom class for the customcell. Here is the code in cellForRowAtIndexPath:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
Scientist *currentScientist = [[xmlParser data] objectAtIndex:indexPath.row];
CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[CustomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
cell.self.cellName.text = currentScientist.self.name;
cell.self.cellSubject.text = currentScientist.self.subject;
cell.self.cellImage.image = currentScientist.self.image;
return cell;
}
I don't know if you need more code to help me.
In my case, creating a different cell identifier for each one worked just fine. I have something like:
NSString *cellIdentifier = [NSString stringWithFormat:#"identifier%i%i", indexPath.section, indexPath.row];
The rest should remain the same.
I found an article that details the problem you are encountering. I would also recommend printing out the Scientist data to ensure an object is being fetched correctly with the objectAtIndex:indexPath.row call.
From the article I linked below, I would be willing to bet that your dequeueReusableCellWithIdentifier is the problem. One quick way you could solve this is by giving each cell its' own unique cell identifier (Just to test out the theory). However, it looks the appropriate way to solve this is by changing up your cellForRowAtIndex method.
Source

Static UITableView created in IB/Storyboarding With dynamic content?

I'm wondering if I could make a basic UITableView and statically set up everything using IB/Storyboarding in Xcode 4.2 but just one of the cells I want to display the current app version. (I'm making an about view) and I don't want to hardcode the app version because I know I'll forget to update it down the road. Is there any way to have that one cell get its data from the dataSource? I tried implementing
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Configure the cell...
if (indexPath.section == 0 & indexPath.row == 0) {
cell.textLabel.text = [self appVersion];
}
return cell;
}
I've tried setting the cellIdentifier on all cells to #"Cell" as well as just setting it on the cell I care about modifying.
and only for that cell returning the app version but that wipes out the content of all the other cells.
If you are using a Static table in Storyboards the static table is the data source.
With a Static Table in Storyboard you would put a UILabel in the cell in question. Than setup a IBOutlet property in your View Controller and hook up the label to the outlet.
Then just update the label to your version number. You can hook up as many outlets as you want to different cells in a static table. Static table just means there is a fixed number of cells , you can still dynamically change the content of those fixed number of cells.
Use different ID for the first row.