UITableViewDelegate methods - iphone

Here is an xcode project that I just did to ask this question:
http://www.mediafire.com/?z26ufsfhby62br9
Open the log and run it. You will find that it outputs display: x and create: x where x is a number 0 to 49 and corresponds to the cell of the same number. It should only output to 22 before any scrolling is performed as Apple are always boasting that their tableviews are loaded as needed.
It basically shows that tableView:willDisplayCell:forRowAtIndexPath: and tableView:cellForRowAtIndexPath: are fired for each cell pretty much as soon as the tableview appears, why is this?
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"create: %i", indexPath.row);
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier] autorelease];
}
// Configure the cell...
cell.textLabel.text = [NSString stringWithFormat:#"%i", indexPath.row];
return cell;
}
- (void) tableView:(UITableView *)tableView
willDisplayCell:(UITableViewCell *)cell
forRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"display: %i", indexPath.row);
}
Why are the above meothds called just after the tableView loads (and just before each cell appears)? Surely they should be called just before each cell appears only?

These are the default delegate methods.. This will be be called for each cell everytime.
You use willDisplayCell:forRowAtIndexPath: to configure things like font and text color. In the newer version of the iPhone, with certain table configurations, if you configure things like the label text color in the tableView:cellForRowAtIndexPath: method, your changes will be lost at some point before the cell is actually displayed. Here you can do things like change the label's color, adjust background highlighting, such things as these.

If your table view is reloading before it actually fully displays, that could cause the behavior you're seeing. Then the cells would get initialized, prepared for display, but then all of that would be lost (before even shown on screen) as the table is reloaded.

Related

Xcode Setting Accessory Mark on Static Cells error

I am having a problem with the Settings page of my app. I have chosen to use static cells so I have a few table sections with 3-4 cells in each.
In viewDidLoad I load my NSUserDefaults and set the accessory marks like this:
...
}
else if ( ... my Condition3 ... ) {
indexPath = [NSIndexPath indexPathForRow:2 inSection:0];
}
UITableViewCell* cell = [settingsTable cellForRowAtIndexPath:indexPath];
cell.accessoryType = UITableViewCellAccessoryCheckmark;
This works fine and it sets the cell with the matching setting with a Checkmark. However, this is only working for the cells that are on screen by default. Any cells which require scrolling do not contain the checkmark.
Is there a way to fix this, preferably without having to use dynamic cells?
Ahh, I finally figured it out. cellForRowAtIndexPath crashes the app so I had to do the following:
I set the cells which require a checkmark in my viewDidAppear method using the code above (in the question). I also remove all checkmarks and then add one to the selected cell in the didSelectRowAtIndexPath method as I was doing before.
In each of the two methods I set an NSString variable to the .textview.text of the selected cell and then implement this method:
-(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
cell.accessoryType = UITableViewCellAccessoryNone;
if ([cell.textLabel.text isEqualToString:[NSString stringWithFormat:#"%#", myFirstString]] || [cell.textLabel.text isEqualToString:[NSString stringWithFormat:#"%#", mySecondString]]) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
}
Hopefully this might help someone.
That's because when you scroll your cells are rebuilt and the checkmarks are cleaned.
You need to set it up in the
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

How to select a UITableView row that is not yet shown?

I'm not sure I'm phrasing my question correctly, so here's the details.
I'm using a UITableView to display the list of available fonts. When the list is dsiplayed,
only about 12 rows show at a time, so if the previously selected font is not yet show, I can't select it when first showing the view.
What I'd like is to have the cell selected and shown in the center of the list when the view appears. But since the UITableView only loads data as needed, this is the best I can get:
EDITED
I've tried this but it doesn't work (the cell is only briefly selected while scrolling):
-(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];
}
[cell.textLabel setText:[fontArray objectAtIndex:indexPath.row]];
[cell.textLabel setFont:[UIFont fontWithName:[fontArray objectAtIndex:indexPath.row] size:16]];
[cell setSelectionStyle:UITableViewCellSelectionStyleBlue];
//select the cell/row if it matches the current font
if([cell.textLabel.text isEqualToString:currentFontName]){
cell.selected=YES;
}
NSLog(#"returning cell %#",cell.textLabel);
return cell;
}
1 - Make your comparison using - (BOOL)isEqualToString:(NSString *)aString
1a - replace your test
if([cell.textLabel.text isEqualToString:currentFontName]){
cell.selected=YES;
}
by
cell.selected = [cell.textLabel.text isEqualToString:currentFontName];
1b - if you need to display your selected font you can do that before loading your TableView:
NSIndexPath * selFntPath = [NSIndexPath indexPathForRow: [fontArray indexOfObject: currentFontName]
inSection: 0];
[tableView scrollToRowAtIndexPath: selFntPath
atScrollPosition: UITableViewScrollPositionMiddle
animated: NO];
2 - Check that you do not unselect your cell in
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath delegate method.
This is a classic behavior in most sample codes.
option: you can keep the select property for user selection and toggle a specific control (ie checkmark using accessoryType property your cell) to show a system-selected row.
This is probably the right approach but you can't test NSStrings for equality by pointer comparison. You want - (BOOL)isEqualToString:(NSString *)aString instead of ==.
I found the solution in another thread - the problem is related to reusing cells. If I do not re-use cells, then everything works properly. Re-using cells also caused problems with multiple checkmarks appearing when only one item is selected. Thanks to those who contributed.
EDIT: If I should not be answering my own questions please tell me...but also tell me the proper way to resolve the question!
EDIT 2: This thread also helped
UITableViewCell going black when selected programmatically

selected UITableViewCell background color changes upon scroll

I've got these cells I have set a custom background colour to. The background colour works fine when I select the cell, however, when I scroll down and back up, two things can happen:
If not many cells are selected, the cells that went out of view sometimes come back with the default blue colour when selected.
If most or all of the cells are selected, the cells that went out come back with one of the colours that is on the cells that were there beforehand - ie. I select all the cells, scroll down and back up and the cells at the top have the same colour as the cells at the bottom (or at least some of them - others retain their own colour).
Here is the code I have that produces this:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *row = [tableView cellForRowAtIndexPath:indexPath];
UIView *backview = [[UIView alloc] initWithFrame:row.frame];
backview.backgroundColor = [colours objectAtIndex:indexPath.row];
row.selectedBackgroundView = backview;
}
That's where the selected method for the cells changes the colour. The cells are created here:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"eventTypeID";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
NSString *sEventType = [[self.eventTypes valueForKeyPath:#"name.text"] objectAtIndex:indexPath.row];
cell.textLabel.text = sEventType;
return cell;
}
And the colours for each cell are set here:
- (void)loadView {
colours = [[NSMutableArray alloc] init];
CGFloat red[] = {0.84,0.86,0.7,0.46,0.56,0.44,0.95,0.91,0.91,0.76,0.06,0.8,0.73,0.0,0.0,0.01,0.18,0.23,0.57,0.18};
CGFloat green[] = {0.12,0.01,0.07,0.17,0.32,0.18,0.49,0.49,0.78,0.61,0.48,0.85,0.85,0.28,0.38,0.53,0.23,0.36,0.32,0.24};
CGFloat blue[] = {0.34,0.5,0.2,0.53,0.55,0.31,0.18,0.18,0.12,0.27,0.14,0.1,0.49,0.1,0.37,0.49,0.4,0.41,0.55,0.40};
for (int i = 0; i < 20; i++) {
[colours addObject: [UIColor colorWithRed:red[i] green:green[i] blue:blue[i] alpha:1.0]];
}
//Get data from server and parse it
...
}
Now, I have only just started programming the iPhone but my guess (and this is a wild one btw) is that the cells are getting re-created in cellForRowAtIndexPath and although some of the properties are getting saved (like the title...) the custom background isn't.
Has anyone come across this behaviour before? If so, how did you solve it?
EDIT: Even weirder behaviour: Sometimes, if you scroll back down and up, the cell that had gone to the "default" selected background colour goes back to it's custom one. The behaviour seems to be random.
Cell background colours are set in many places, to ensure that the background displayed is the one you want you need to use:
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
see this question for more details. If you require custom selection colours, then you should subclass UITableViewCell and override - (void)setSelected:(BOOL)selected and - (void)setHighlighted:(BOOL)highlighted

UITableview strange behaviour

I have a UITableView with a top navigation bar. The data for the UITableView comes from an array which contains more than 20 objects.
Everything is fine so far. However sometimes when i do repeated scrolling (fast), I find last row being repeated, sometimes gets cut into half, overwritten.
I am new to iPhone development and have no clue why this happens.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
return [appDelegate.preList count];
}
// Customize the appearance of table view cells.
- (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];
}
// Configure the cell...
Item *i=[appDelegate.preList objectAtIndex:indexPath.row];
cell.textLabel.text=i.iName;
cell.accessoryType=UITableViewCellAccessoryNone;
return cell;
}
I have attached a screenshot of my problem as well. Notice how the navigation bar and the last cell are.
Help would be greatly appreciated
This looks like you have added two identical table views to your view. If you place a break point on numberOfSections… and then inspect the tableView object, I bet you will find two different table views. Start by looking for places in your code where you [self.view addSubview:tableView] or something similar. Remember, if you are using IB to setup your table, you do not need to programmatically add it.
I think you are seeing this when scrolling fast because only then the tableView bounces leaving some content offset. You have probably added 2 tableViews back to back.

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.