I have the following code which is trivial at first sight. I simply set want to set the font type to Georgia with a size of 14 if the cell is from the result of a search or if there is a count of zero in my students array.
However, with this particular code cell that's last in my tableView is taking on the font of Georgia with size 14. All other cells are working proper. Where in my code is the logic wrong?
- (UITableViewCell *)tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSInteger row = [indexPath row];
NSInteger section = [indexPath section];
static NSString *CellIdentifier = #"Student";
cell = [tv dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
}
// Configure the cell
if([studentsSearch count] > 0) {
cell.text = (NSString *)[[[studentsSearch objectAtIndex:section] objectAtIndex:row] valueForKey:#"name"];
cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
} else {
if(isSearching == YES)
cell.text = #"No students available.";
else
cell.text = #"No students have been added for this school.";
cell.font = [UIFont fontWithName:#"Georgia" size:14];
cell.accessoryType = UITableViewCellAccessoryNone;
}
return cell;
}
EDIT
What appears to be happening is when the view controller gets instantiated and pushed on top of the navigation controller's stack, my studentsSearch array is nil. I populate it within that controller.
So upon initialization, the cell has its font set to Georgia with a size of 14 because the count is < 0. However, once I populate the studentsSearch array and reload the tableView's data, the font seems to be sticking from when the view first got initialized.
I suppose now I need to find how to set the font back to that cell to what the default is.
I'm not quite sure what you're asking, but I do note that you're only setting the font to Georgia 14 when you have a search result; otherwise, you're ignoring it. If you have a cell with it's font set in the second if/then branch, and then retrieve that cell (using dequeueReusableCellWithIdentifier:), it will already have it's font set.
The simplest solution is to add
cell.font = [UIFont systemFontOfSize: 14];
after
cell.text = (NSString *)[[[...
cell.accessoryType = ...
in the first branch.
Keep in mind that table cells are recycled. Let's say your table has 15 visible rows. That means you have approximately 15 cells (or a few more) that get created and, as I said, recyled. Even if your table has hundreds of rows, it will still use the same 15 cells.
In this case, you're never resetting the font size, so once you set that font size on a cell, it will be used on any row after it that re-uses the cell.
So, if your studentsSearch count > 0, you need to make sure to set the font size to whatever your baseline is (17?).
I'd suggest that you identify the 'special' cell by giving it a different cell identifier.
In this case, you'd request the special cell with cell reuse identifier, e.g. #"None", and if cell has not yet been created, then create one and set its font.
This way, you create an extra cell with a special identifier, and it is kept separate from the other regular cells in your table.
- (UITableViewCell *)tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSInteger row = [indexPath row];
NSInteger section = [indexPath section];
static NSString *StudentCellIdentifier = #"Student";
static NSString *NoneCellIdentifier = #"None";
// did we find students?
BOOL found = [studentsSearch count] > 0;
// get/create correct cell type
cell = [tv dequeueReusableCellWithIdentifier:(found ? StudentCellIdentifier : NoneCellIdentifier)];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithFrame:CGRectZero
reuseIdentifier:(found ? StudentCellIdentifier : NoneCellIdentifier)];
}
// return a student, or None cell if no studnts found
if( found )
{
cell.text = (NSString *)[[[studentsSearch objectAtIndex:section] objectAtIndex:row] valueForKey:#"name"];
cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
}
else
{
if(isSearching == YES)
cell.text = #"No students available.";
else
cell.text = #"No students have been added for this school.";
cell.font = [UIFont fontWithName:#"Georgia" size:14];
cell.accessoryType = UITableViewCellAccessoryNone;
}
return [cell autorelease];
}
Related
hello friends I have created custom table and add all needed delegate and datasource for table then this is working good but when we scroll my table then content change like this is code...
Table_worklist=[[UITableView alloc]initWithFrame:CGRectMake(10,480,750,130)style:UITableViewStylePlain];
Table_worklist.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
Table_worklist.delegate=self;
Table_worklist.dataSource=self;
Table_worklist.layer.borderWidth = 2.0;
Table_worklist.layer.borderColor = [UIColor grayColor].CGColor;
[ScrollView addSubview:Table_worklist];
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *MyIdentifier = #"MyIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell== nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier] ;
cell.selectionStyle = UITableViewCellSelectionStyleNone;
UITextField *resultval=[[UITextField alloc]initWithFrame:CGRectMake(510,10,120,30)];
resultval.tag=1;
resultval.font = [UIFont boldSystemFontOfSize:12.0];
//the horizontal alignment of the text
resultval.textAlignment = NSTextAlignmentCenter;
resultval.contentVerticalAlignment = UIControlContentHorizontalAlignmentCenter;
resultval.clearButtonMode = UITextFieldViewModeWhileEditing; // has a clear 'x' button to the right
resultval.delegate =self;
if([[NSString stringWithFormat:#"%#",[[TestSample objectAtIndex:indexPath.row]valueForKey:#"ResultType"]]isEqualToString:#"Numerical"])
{
imageLayer = field.layer;
[imageLayer setCornerRadius:02];
[imageLayer setBorderWidth:1];
imageLayer.borderColor=[[UIColor lightGrayColor]CGColor];
}
else if([[NSString stringWithFormat:#"%#",[[TestSample objectAtIndex:indexPath.row]valueForKey:#"ResultType"]]isEqualToString:#"Words"]||[[NSString stringWithFormat:#"%#",[[TestSample objectAtIndex:indexPath.row]valueForKey:#"ResultType"]]isEqualToString:#"Comment"])
{
imageLayer = field.layer;
[imageLayer setCornerRadius:06];
[imageLayer setBorderWidth:1];
imageLayer.borderColor=[[UIColor blackColor]CGColor];
UIButton *CheckMark=[[UIButton alloc]initWithFrame:CGRectMake(540,10,30,30)];
CheckMark.tag=1;
[CheckMark setImage:[UIImage imageNamed:#"DownTriangle1"]forState:UIControlStateNormal];
imageLayer = CheckMark.layer;
[imageLayer setCornerRadius:02];
[imageLayer setBorderWidth:1];
imageLayer.borderColor=[[UIColor blackColor] CGColor];
[cell.contentView addSubview:CheckMark];
}
[cell.contentView addSubview:resultval];
}
return cell;
}
this show right data for first 3or4 cell(because table height 130 so show only 3 cell at a time )for these 3 cell my table show right data ...for example
if([[NSString stringWithFormat:#"%#",[[TestSample objectAtIndex:indexPath.row]valueForKey:#"ResultType"]]isEqualToString:#"Numerical"])
{ }
then data is numerical go this if condition other wise go else condition......
but when we have for more cell like as 7-8 cell then when we scroll these cell so what happened suppose at cell-4 my controller go else if condition forComment and word data .........so at last cell -8 got numerical data so controller go in if condition but check mark button which was created in if else condition which show on my if condition data ......so i don't know what happened
when we scroll how to change my condition ......
Actually i thing when last created else if condition (which create uibutton checkmark)on cell-4 which are apply on cell-8...........but controller go in right place in if condition at cell-8 .....but last time created checkmark button generated on cell-8 ......
how to solve this problem....
Before You follow my Answer i want to tell you that following code is bad for memory management because it will create new cell for each rows of UITableView, so be careful for it.
But it is better to use, When UITableView Have Limited rows (about 50-100 may be ) then following code is helpful in your case, use it if is it suitable for you.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *CellIdentifier = [NSString stringWithFormat:#"S%1dR%1d",indexPath.section,indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil)
{
cell = [[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
/// Put your code here
}
return cell;
}
If you have limited rows then this is best code for you.
I have a UITableView that displays content programmatically.
The cells load fine when first opened, but after scrolling they all show the same caption, which is, I assume, the last cells caption.
The underlying TableView that opens if a cell is selected still works fine.
I looked up the solutions on SO but they either didn't work for me or I'm too dumb to understand the Problem, which sees to be the reusing the cells for some kind of saving resources.
I will post the cellForRowAtIndexPath as I assume the Problem lies there, if any further code is needed please let me know.
- (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];
}
NSString *oneLine = [entrys objectAtIndex:position];
NSArray *lineComponents = [oneLine componentsSeparatedByString:#";"];
cell.textLabel.text = [lineComponents objectAtIndex:0];
cell.textLabel.textColor = [UIColor colorWithRed:0.0 green:0.8 blue:0.2 alpha:1.0];
if (position < [entrys count] -2 ) {
position++;
}
return cell;
}
Thanks in advance
You're not using the indexPath parameter at all. It should proably be:
NSString *oneLine = [entrys objectAtIndex:indexPath.row];
try this one instead of postion
NSString *oneLine = [entrys objectAtIndex:indexpath.row];
NSArray *lineComponents = [oneLine componentsSeparatedByString:#";"];
cell.textLabel.text = [lineComponents objectAtIndex:0];
cell.textLabel.textColor = [UIColor colorWithRed:0.0 green:0.8 blue:0.2 alpha:1.0];
or
if it still not work then your array is not initialized properly. initialize it
The problem is here
if (position < [entrys count] -2 ) {
position++;
}
your rows in table are larger than the array count entrys and due to this condition your position is incremented to last object and after that this condition never gets called thus your position points to last object.
Could you tell what you have returned from your tableView:numberOfRowsInSection: method ?
Below is code for UITableView, But when i scroll its behaves weirdly (too annoying)... This problem is due to reuseIdentifier.... but dont know how to solve..
- (UITableViewCell *)tableView:(UITableView *)tableView1 cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView1 dequeueReusableCellWithIdentifier:CellIdentifier];
NSInteger imgTag = 1;
NSInteger lblTag = 2;
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
UIImageView *imgView = [[UIImageView alloc] initWithFrame:CGRectMake(2, 2, 52, 52)];
// Image:[UIImage imageNamed:[self.glassType objectAtIndex:indexPath.row]]];
imgView.tag = imgTag;
[cell.contentView addSubview:imgView];
[imgView release];
UILabel *lblName = [[UILabel alloc] initWithFrame:CGRectMake(60, cell.frame.size.height/4, 200, 21)];
// lblName.text = [self.glassName objectAtIndex:indexPath.row];
lblName.tag = lblTag;
[cell addSubview:lblName];
[lblName release];
}
NSInteger imgIndex = 2;
NSInteger lblIndex = 3;
((UIImageView *)[cell viewWithTag:imgTag]).image = [[self.glassType objectAtIndex:indexPath.row] objectAtIndex:imgIndex];
((UILabel *)[cell viewWithTag:lblTag]).text = [[self.glassName objectAtIndex:indexPath.row] objectAtIndex:lblIndex];
return cell;
}
- (void)tableView:(UITableView *)tableView1 didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView1 cellForRowAtIndexPath:indexPath];
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
How to make Cell for row at index so that it remains constant even when scrolled??? Also how to make single selection in UITableView??
The answer is that you should not add subviews to your table cells outside of the "if (cell == nil) { ..." clause or they get added over and over again to the same cell when it gets re-used.
See my answer to this question for a more detailed explanation, including code for how to fix it:
cellForRowAtIndexPath memory management
You also cannot store state in table cells because as soon as they scroll offscreen they are recycled and re-appear at a different index in your table. You need to set up an array of model objects to store state for your table cells (such as what their accessory type should be). A more detailed explanation can be found in my answer to this question:
Looping through UITableViewCells of a UITableView
If you fix how you are adding subviews to the cells, and store your "ticked" state in an array of model objects as well as setting the cell.accessoryType (so that it can be restored when the cell is dequeued), then your approach to row selection is otherwise correct.
So put this in your tableView:cellForRowAtIndexPath: method, just before the return cell;:
MyModelObject *object = [self.arrayOfModelObjects objectAtIndex:indexPath.row];
BOOL isChecked = object.checked;
cell.accessoryType = isChecked? UITableViewCellAccessoryCheckmark: UITableViewCellAccessoryNone;
And in your tableView: didSelectRowAtIndexPath: method, get rid of the current logic and replace it with:
- (void)tableView:(UITableView *)tableView1 didSelectRowAtIndexPath:(NSIndexPath *)indexPath
for (int i = 0; i < [self.arrayOfModelObjects count]; i++)
{
MyModelObject *object = [self.arrayOfModelObjects objectAtIndex:i];
object.checked = (i == indexPath.row); // only check the one we just tapped
}
//refresh table to update the accessory views for all rows
[tableView1 reloadData];
}
Obviously replace the arrayOfModelObjects with your own model implementation. You could just use an array of NSNumber objects containing bools if you don't want to create a custom class for this purpose.
The recycling queue is like a pool where previously created Cells are stored before to reuse them. For example when you scrolls up, at the moment the cell disappears above, it is stored in the queue and becomes available for the cell that will appear at the bottom. Ok ?
Actually the number of cells really created is exactly the max simultaneous cell you can display in your table (in most cases from 3 to 8). In other words your if (cell == nil) code is executed (more or less from 3 to 8 times) at the first reloadData to create the pool of cells your table needs.
Then all you make on a cell is kept as it and appears again when you dequeue it. It's now easy to understand that, in your code, you have to make all strictly row-dependant settings outside the if (cell == nil) block. The same way, do not add subViews outside the if (cell == nil) block, you can imagine the thousands of subview you will add each time you reset a dequeued cell !
Tip: if you need some custom cleanup before reusing a cell (like to set an image to blank), you can create a custom UITableviewCell class and implements the prepareForReuse method.
Is it clear ?
Always reload your tableView in viewWillAppear method instead of viewDidLoad.
(void) viewWillAppear:(BOOL)animated{
[self.tableView reloadData];
}
This avoids most of all unexpected and annoying problems. :)
I have an tableview which is loaded from a mutablearray as listed below. However I need to asign each item in the array an ID and have a tickbox next to the item. Basically it's preferences for our search, it lets users prioritise by whichever tickboxes are ticked. So I'll want to save which items are ticked to a plist or similar.
Heres how the array is loaded:
arryTableIconsText = [[NSMutableArray alloc] init];
[arryTableIconsText addObject:#"Facilities for partially sighted or blind people"];
[arryTableIconsText addObject:#"An 'assistance dogs welcome' policy"];
[arryTableIconsText addObject:#"Disabled access facilities for wheelchair users (with assistance)"];
*more items added here*
arryTableIcons = [[NSMutableArray alloc] init];
[arryTableIcons addObject:#"visuallyImpaired_off.png"];
[arryTableIcons addObject:#"guidedogs_off.png"];
[arryTableIcons addObject:#"wheelchairassist_off.png"];
*more items added here*
And then loaded in to a table like so:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
}
cell.textLabel.lineBreakMode = UILineBreakModeWordWrap;
cell.textLabel.numberOfLines = 0;
cell.textLabel.font = [UIFont fontWithName:#"Helvetica" size:17.0];
cell.textLabel.text = [arryTableIconsText objectAtIndex:indexPath.row];
cell.imageView.image = [UIImage imageNamed:[arryTableIcons objectAtIndex:indexPath.row]];
return cell;
}
The result is the following:
But I don't know where to go from here to convert it in to a checkbox to the right of each cell with the ID saved?
Any tips really will be appreciated, Tom
Use an NSMutableIndexSet instance variable and populate it with the index of the cells being checked.
Then in the cellForRowAtIndexPath method, set the accessory type of the cell to UITableViewCellAccessoryTypeCheckmark or UITableViewCellAccessoryTypeNone depending on whereas the indexPath.row is in the NSMutableIndexSet or not.
Finally, when the cell is tapped, add the indexPath.row to the indexset if not alread, or remove it if it already was present, to toggle the status of the corresponding cell, then call reloadData on the tableView.
I see in your code too that you are not familiar with the reuse mechanism of UITableViewCells. You should read the "Table View Programming Guide" in Apple's documentation and learn how to implement cellForRowAtIndexPath in a more efficient and reactive way (in term of reactivity and memory footprint)
Example
// Let selectedCellIndexes be an instance variable in your .h of type NSMutableIndexSet*
// Initialize it (probably at the same place you initialise your texts & icons, once for all, probably in your init method
selectedCellIndexes = [[NSMutableIndexSet alloc] init];
Then to fill the cells:
-(UITableViewCell*)tableView:(UITableView*)tv cellForRowAtIndexPath:(NSIndexPath*)indexPath {
// Try to recycle and already allocated cell (but not used anymore so we can reuse it)
UITableViewCell* cell = [tv dequeueCellWithReuseIdentifier:...];
if (cell == nil) {
// If we didn't manage to get a reusable (existing) cell to recycle it
// then allocate a new one and configure its general properties common to all cells
cell = [[[UITableViewCell alloc] initWithStyle:... reuseIdentifier:...] autorelease];
// ... configure stuff that are common to all your cells : lineBreakMode, numberOfLines, font... once for all
cell.textLabel.lineBreakMode = UILineBreakModeWordWrap;
cell.textLabel.numberOfLines = 0;
cell.textLabel.font = [UIFont fontWithName:#"Helvetica" size:17.0];
}
// Then here change the stuff that are different between each cell
// (this code will be executed if the cell has just been allocated as well as if the cell is an old cell being recycled)
cell.textLabel.text = [arryTableIconsText objectAtIndex:indexPath.row];
cell.imageView.image = [UIImage imageNamed:[arryTableIcons objectAtIndex:indexPath.row]];
cell.accessoryType = [selectedCellIndexes containsIndex:indexPath.row] ? UITableViewCellAccessoryTypeCheckmark : UITableViewCellAccessoryTypeNone;
return cell;
}
And finally, to toggle the checkmarks:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if ([selectedCellIndexes containsIndex:indexPath.row]) {
[selectedCellIndexes removeIndex:indexPath.row];
} else {
[selectedCellIndexes addIndex:indexPath.row];
}
[tableView reloadData];
}
I have created a TableView in my application with 5 sections in it.
Sections 1 - 4 only contain one row each for the minute and section 5 contains 5 rows.
Everything works fine until I scroll the TableView off the screen. In my first section and row (cell) I have the accessoryView set to a UILabel with some text in it.
Every other cell has the disclosure button as the accessoryType.
When I scroll the tableView the text I have in the first cell somehow appears in the the last cell!?
I have set up my data by adding NSStrings to array's and then adding them as dictionaries to an NSMutableArray.
And here is my cell set up:
- (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];
}
// UISwitch *aUISwitch = [[[UISwitch alloc]initWithFrame:CGRectZero]autorelease];
// Configure the cell.
NSDictionary *dictionary = [listOfSettings objectAtIndex:indexPath.section];
NSArray *array = [dictionary objectForKey:#"Settings"];
NSString *cellValue = [array objectAtIndex:indexPath.row];
cell.textLabel.text = cellValue;
if([cellValue isEqualToString:#"Status"]){
UILabel *viewLabel = [[UILabel alloc]initWithFrame:CGRectMake(0, 0, 100, 20)];
[viewLabel setText:#"Connected"];
cell.accessoryView = viewLabel;
[viewLabel release];
}
else{
cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
}
return cell;
}
I know cells get deleted/removed when they go off screen so I presume this has something to do with it? What is the recommended practice for dealing with cells that go off screen and reappear?
just at quick glance... in the else statement, not only do you need to set the cell.accessoryType, but also set the cell.accessoryView=nil;
the accesoryView is still there as the cell was recycled.