custom UITable cell how to build with unique included object state? - iphone

So I has custom table cell defined via builder and loaded via nib (and it has a property of its idexPath) and it has 2 buttons. I want to show table with dynamically changed state of those buttons. IE 1st cell - both enabled, 2nd cell - both buttons disabled, 3rd - 1st btn enabled and 2nd btn disabled and so on.
Now if I use 1 reuse identifier all the cells will look the same which I don't want. I want every cell has its own view which means unique reuse id for every cell.
But how to reach this? If I'll create some unique cellId at
- (UITableViewCell *)tableView:(UITableView *)_tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
which will be a string then I could not create the same string in meaning of objects. I could create string with same text but this will be another object so I couldn't get the previously created cell with such cellId again via reuseId. So i cant change buttons state of one cell and update it then with
[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:path] withRowAnimation:UITableViewRowAnimationNone];
but only the [tableView reloadData]; will work.

I have a feeling that you're only setting the state of your cell's buttons when you first create the cell with initWithStyle:reuseIdentifier:. This is the wrong way to go about things. You need to set the state for your cells in every call to cellForRowAtIndexPath, whether they're being re-used or not. In your case, if each cell has the same UI (two buttons) then they should all share one reuseIdentifier. Your datasource should be responsible for maintaining you cells' states, not the UITableViewCell objects.
It's the difference between this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
myCustomCell *cell = (myCustomCell *)[myTable dequeueReusableCellWithIdentifier:#"myCellIdentifier"];
if (cell == nil) {
// Load cell from nib here and set the cell's button states based on indexPath
}
return cell;
}
and this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
myCustomCell *cell = (myCustomCell *)[myTable dequeueReusableCellWithIdentifier:#"myCellIdentifier"];
if (cell == nil) {
// Load cell from nib here
}
// set the cell's button states based on indexPath here
return cell;
}

Related

Modify existing reusable cells with cellForRowAtIndexPath: method

I use tableview with reusable cells. On each cell I have a textField with text, which I can modify. If text is empty, I delete that cell.
Lets say that we had 100 rows and we want to modify row number 1: we tap on it, give an empty string #"", scroll down to position number 50 and tap on this cell.
What now is going is that we detect tap gesture on another cell and I call method textFieldDidEndEditing: to see should I remove this cell from tableview. I use cellForRowAtIndexPath: to get the modified cell.
The problem is that there appear other cells with empty textField. I delete modified cell, but only one. I think that this is a problem with reusable cells.
Can anybody can help me with this problem?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *ImageIdentyfier = #"StandardCellWithImageIdentifier";
StandardCellWithImage *cellImage = (StandardCellWithImage *)[tableView dequeueReusableCellWithIdentifier:ImageIdentyfier];
if(cellImage == nil) {
cellImage = [[StandardCellWithImage alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ImageIdentyfier];
}
cellImage.nameLabel.delegate = self;
Item *item = [self.mutableFetchResults objectAtIndex:indexPath.row];
cellImage.nameLabel.text = item.itemText;
cellImage.infoLabel.text = item.itemInfo;
cellImage.checkbox.userInteractionEnabled = YES;
cellImage.nameLabel.userInteractionEnabled = NO;
if(item.itemIsChecked.boolValue == YES) {
cellImage.checkbox.tag = indexPath.row;
[cellImage.tapGesture addTarget:self action:#selector(didSelectedImageAtIndexPath:)];
cellImage.checkbox.image = [UIImage imageNamed:#"checkbox-checked.png"];
} else {
cellImage.checkbox.tag = indexPath.row;
[cellImage.tapGesture addTarget:self action:#selector(didSelectedImageAtIndexPath:)];
cellImage.checkbox.image = [UIImage imageNamed:#"open-checkbox.png"];
}
return cellImage;
}
When you scroll from row 1 to row 50, already existing cells are reused - including your cell with empty textField. That is why you see it several times and why your delete routine removed only one instead of all.
Sounds like your cell creation at cellForRowAtIndexPath method needs fixing to make sure empty textfield is not automatically copied to recycled cells. Without seeing any code, this exercise is left to you.
Looked at code, thanx. Could not see any "easy" fix, so proposing that you should avoid the problem. So instead of checking cell taps, maybe you should check list scrolling.
The problem you have exists only because cell, which was being edited, was recycled due user scrolling the list. Therefore remove the problem by a) don't let user to scroll while editing text or b) stop text edit when user starts scrolling.
when your textFieldDidEndEditing is invoked finish , you should check whether the text is "" if it is "" I think you should delete it from the dataSource and then reloadData
your - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath method should write like this :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
XXXXXXXCell *cell = [tableView dequeueReusableCellWithIdentifier: kIdentifier];
if (cell == nil) {
//Init cell, only init
}
//Setup the cells detail, such as the textField.text and so on
return cell;
}

Prototype cells different from dynamically created cells

I have created two types of prototype cells in storyboard. The dimension of one of them have been customized to accomodate UIButton object. However when the cells are created, they have the standard height. I can see the UIButton object but it gets truncated because of the cell height.
Why are the newly created cells different from the prototype cells?
The relevant section of the code is as follows:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell;
if(cell == nil)
{
cell = [tableView dequeueReusableCellWithIdentifier:#"PictureSelectionCell"];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
pictureButtonProperty = (UIButton *) [cell viewWithTag:1];
}
}
Going forward, what are my options for creating the cell of the width (or dimensions) defined in the storyboard? Programmatically, I will be able to achieve this by creating a CGRect object with the specified dimensions and then create a cell using initWithFrame. However, I would like to avoid doing things manually.
Thanks for your response.
first of all you can always set it with code
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return YOUR_ROW_HEIGHT;
}
other way if you choose your UITableView if the storyboard, under the size inspector change the Row Height.

UITableCell Reusing itself

I made a UITableViewCell and I have 20 rows in that table in which 5 at a time on screen.
I have set accessory view checkd mark in didSelect delegate method on which row is being selected. My concern is let suppose first row is selected and its accessory type is checked, now if i scroll the table we see the sixth one is also checked. I know that the cell is reusing itself and not creating itself again.
The model should be able to handle which cell is checked and which is not. To simplify the problem, you can keep an array which will the NSIndexPaths that should be checked. If only one can be checked at the time, an ivar of the type NSIndexPath is more than enough.
- (void)tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([[aTableView cellForRowAtIndexPath:indexPath] accessoryType] == UITableViewCellAccessoryCheckmark)
{
// Ok this one is selected, so we will remove it from the Reference Array.
}
else
{
// Ok this one doesn't has a checkMark
// First add the checkmark
[[aTableView cellForRowAtIndexPath:indexPath] setAccessoryType:UITableViewCellAccessoryCheckmark];
// Add the NSIndexPath to the Array of references
}
}
In the delegate method
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
NSString *cellId = [NSString stringWithFormat:#"cell%d",indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellId ];
if (cell==nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1
reuseIdentifier:cellId ] autorelease];
}
}
Set different cell ID to different cell row.

Custom CellIdentifier is Null When Using Search Display Controller

In my tableview have custom cells that I initialize from a UITableViewCell class. I have sections for first letters of records and have an indexPath that is being created dynamically.
I wanted to add a search display controller to my tableview. So I did, created all methods to filter data. I am sure that my functions are working well because I am printing array count to screen for search results.
My problem is that the first time view loads, the data is on the screen. But when I hit the search input and type a letter, than I get 'UITableView dataSource must return a cell from tableView:cellForRowAtIndexPath:' error. After I used a breakpoint I saw that my custom cell is nil after searching. Data is exist, but cell is not being initialized.
Here is the code I use for custom cell initializing:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"ObjectCell";
SpeakerCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
NSDictionary *myObject = [[sections valueForKey:[[[sections allKeys] sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)] objectAtIndex:indexPath.section]] objectAtIndex:indexPath.row];
cell.label1.text = [myObject objectForKey:#"myValue"];
return cell;
}
I believe I made a mistake when putting controls in IB. So I added screenshots of objects:
Connections inspector for my table view
Connections inspector for my search display controller
EDIT: Problem is actually solved, I have used a UISearchBar instead of Search Display Controller but I guess this issue remains unsolved. So I'm willing to try any ways to make it work.
As of here search display controller question,
you need to access the self.tableView instead of tableView:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:#"CellId"];
// do your thing
return cell;
}
For those using iOS 5 and StoryBoards, you would want to use the following method instead of initWithIdentifier:
initWithStyle:(UITableViewCellStyle)stylereuseIdentifier:(NSString *)reuseIdentifier
Example:
NSString *cellIdentifier = #"ListItemCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
I'm not sure about how this should work in storeboarding.
But normally you would check if the [tableView dequeueReusableCellWithIdentifier:CellIdentifier] returns a cell.
Because if the cell in not loaded before or there aren't any cells to reuse you will have to create a new cell:
SpeakerCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[SpeakerCell alloc] initWithIdentifier: CellIdentifier];
}
Also when in declaring local variables in Objective-C we tent not to capitalize the first letter.
I had the same issue, with custom cells (built in Storyboard) not being drawn as soon as the first letter was put in the search field. The search was successful however.
Finally I found a good tutorial from Brenna Blackwell suggesting to configure manually the cell drawing in the corresponding subclass of UITableViewCell, adding UILabels and other items.

Checkbox cell in a table view: User can't check it

I need help in using checkbox cell. I currently added the object to tableview. It looks ok until i tried building and running the program where I cannot check the checkbox. I am currently using a tableview which displays items runtime with a checkbox for each item so i can have multiple selections.
I am new to xcode and I have been stuck for a week with this problem. i tried google but still no luck.
Any snippets, answers, or explanations is very much appreciated.
First we need to edit this method: - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath. Assuming you generated a Navigation-based application, this method should already be there, only commented out. I don't know the exact details of your implementation, but you somehow have to keep track of the checkbox state for each cell in the tableView. For example, if you had a BOOL array, the following code would work:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (checkboxArray[indexPath.row])
checkboxArray[indexPath.row] = NO;
else
checkboxArray[indexPath.row] = YES;
[self.tableView reloadData];
}
Now that we know what cells need to have a checkmark next to them, the next step is to modify how the cell is displayed. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath handles the drawing of each cell. Building off the previous example, this is how you would display the checkbox:
- (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] autorelease];
}
if (checkboxArray[indexPath.row]) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
else
cell.accessoryType = UITableViewCellAccessoryNone;
// Configure the cell.
return cell;
}
If we don't call reloadData, the checkmark will not show up until it goes off-screen and reappears. You need to explicitly set the accessoryType each time because of the way cells are reused. If you set the style only when a cell is checked, other cells that may not necessarily be checked will have a checkmark when you go to scroll. Hopefully this gives you a general idea on how to use checkmarks.