I've got a tableview loaded from an array and for some reason it's re-using the cell but loading the same information lower down the table, a second time.
http://screencast.com/t/Ig8bcqSpLzp
The video above should give you an idea of what I mean.
This is my code to load the cell:
- (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];
}
accessicon *current =[arryAccess objectAtIndex:indexPath.row] ;
cell.textLabel.text = current.text;
cell.imageView.image = [UIImage imageNamed: current.iconPath];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
mySwitch *switchView = [[mySwitch alloc] initWithFrame:CGRectZero];
cell.accessoryView = switchView;
switchView.myValue = current.iconValue;
if(totalIconValue & current.iconValue) {
[switchView setOn: YES animated:NO];
} else {
[switchView setOn: NO animated:NO];
}
[switchView addTarget:self action:#selector(switchChanged:) forControlEvents:UIControlEventValueChanged];
[switchView release];
return cell;
}
Any help will be appreciated
EDIT: I've updated my code above, however on the actual device it's still selecting items it shouldn't be - no longer reusing the names but if i scroll up and down it selects them on it's own?
see the video here: http://screencast.com/t/a9N1qbws
Tom
This code:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
if (cell == nil) {
...
is fundamentally wrong. It should read:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
...
And then, anything that is row-specific should be outside that if statement.
The pattern for this, generally, should be:
dequeue a reusable cell
if you didn't get one:
allocate a new cell
perform generic cell setup, i.e. setup that applies to all cells
setup the cell for the specific row, and return it.
Hope this helps.
You aren't reusing you cells. And you are filling the cells only when the init fails. Try this code:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
}
accessicon *current =[arryAccess objectAtIndex:indexPath.row] ;
cell.textLabel.text = current.text;
cell.imageView.image = [UIImage imageNamed: current.iconPath];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
mySwitch *switchView = [[mySwitch alloc] initWithFrame:CGRectZero];
cell.accessoryView = switchView;
switchView.myValue = current.iconValue;
if(totalIconValue & current.iconValue) {
[switchView setOn: YES animated:YES];
} else {
[switchView setOn: NO animated:YES];
}
[switchView addTarget:self action:#selector(switchChanged:) forControlEvents:UIControlEventValueChanged];
[switchView release];
Or develop under ios5 and get rid of the alloc and init.
You are constantly creating new rows instead of reusing the old ones.
Move the line
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
inside the if block, end move everything else you have in the if block outside of it, except the lines
mySwitch *switchView = [[mySwitch alloc] initWithFrame:CGRectZero];
cell.accessoryView = switchView;
You need to create the switch only once for row, when it is initialized (that means, in the if statement). I suggest you to create it and assign it a tag, so you can access it later by its tag. Your code now is creating a new switch everytime the cell is used, this could be the reason why the switches are behaving this way.
Related
I have a UItableView where every UITableViewCell is containing a UISwitch .Now my question is when i will click in one switch then how can i OFF other switches of the UITableViewCell
In my code i have already made the view and i can ON/OFF the switches.But i want to OFF all other switches except my selected switch.
Please some help me by giving an example or source code example.
With Best Regards
Edit
My Code:
- (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];
switchview = [[UISwitch alloc] initWithFrame:CGRectZero];
cell.accessoryView = switchview;
switchCondition = NO;
[switchview setOn:NO animated:YES];
[switchview addTarget:self action:#selector(updateSwitchAtIndexPath:) forControlEvents:UIControlEventValueChanged];
[switchview release];
}
if(switchCondition == YES){
[switchview setOn:YES animated:YES];
}
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.contentView.backgroundColor = [UIColor clearColor];
cell.textLabel.text = [NSString stringWithFormat:#"%#",[cellValueArray objectAtIndex:indexPath.row]];
return cell;
}
- (void)updateSwitchAtIndexPath:(UISwitch*)sender {
if(sender.on){
switchCondition = YES;
[table reloadData];
}
}
Update your data model used by the table's data source, then reload the table (or at least the visible rows). This will cause each row to reload and each switch will get updated with the latest data.
Edit: Here's an updated version of your code:
You need an instance variable to track the state of each switch. Create an array to hold the YES and NO values. In the code below I will assume there is an instance variable named switchConditions of type NSMutableArray that has been setup with NSNumber objects representing the YES and NO values for each row. This is similar to your cellValueArray. You should also get rid of your switchView and switchCondition instance variables.
- (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];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
UISwitchView *switch = [[UISwitch alloc] initWithFrame:CGRectZero];
cell.accessoryView = switch;
[switchview addTarget:self action:#selector(updateSwitchAtIndexPath:) forControlEvents:UIControlEventValueChanged];
[switch release];
}
UISwitchView *switch = (UISwitchView *)cell.accessoryView;
switch.tag = indexPath.row; // This only works if you can't insert or delete rows without a call to reloadData
BOOL switchState = [switchConditions[indexPath.row] boolValue];
switch.on = switchState; // this shouldn't be animated
cell.contentView.backgroundColor = [UIColor clearColor];
cell.textLabel.text = cellValueArray[indexPath.row];
return cell;
}
- (void)updateSwitchAtIndexPath:(UISwitch*)switch {
NSInteger row = switch.tag;
if (switch.on){
// This switch is on, turn all of the rest off
for (NSUInteger i = 0; i < switchConditions.count; i++) {
switchConditions[i] = #NO;
}
switchConditions[row] = #YES;
[self.tableView reloadData];
} else {
switchConditions[row] = #YES;
}
}
i am new in iPhone development. I am developing an app which contains tableview with label inside the cell. I have placed different label for each row like this if(indexpath.row == 0) like this for each cell row i have done it. But when i am scrolling the tableview my label are getting mixed. Please help me!
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
if (indexPath.row == 2)
{
pendingListNumberLabel = [[UILabel alloc] init];
pendingListNumberLabel.font = [UIFont fontWithName:#"Helvetica" size:14];
pendingListNumberLabel.textAlignment = UITextAlignmentLeft ;
pendingListNumberLabel.textColor = [UIColor orangeColor];
pendingListNumberLabel.frame = CGRectMake(240, 6, 110, 30);
[pendingListNumberLabel setBackgroundColor:[UIColor clearColor]];
[cell addSubview:pendingListNumberLabel];
}
cell.textLabel.text = [affiliationsArray objectAtIndex:indexPath.row];
[cell setAccessoryType:UITableViewCellAccessoryDisclosureIndicator];
return cell;
You are adding the UILabel as a subview, that is the mistake here.
Normally you would use a custom UITableViewCell for this kind of stuff, but a quick fix is this:
if (indexPath.row == 2)
{
pendingListNumberLabel = [[UILabel alloc] init];
pendingListNumberLabel.font = [UIFont fontWithName:#"Helvetica" size:14];
pendingListNumberLabel.textAlignment = UITextAlignmentLeft ;
pendingListNumberLabel.textColor = [UIColor orangeColor];
pendingListNumberLabel.frame = CGRectMake(240, 6, 110, 30);
[pendingListNumberLabel setBackgroundColor:[UIColor clearColor]];
pendingListNumberLabel.tag = 1337
[cell addSubview:pendingListNumberLabel];
} else {
for (UIView * subView in [cell subviews]) {
if ([subView tag] == 1337) {
[subView removeFromSuperview];
}
}
}
This is happening because you are using reusable cells and not in a right way ..
just replace your code -
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
with this -
UITableViewCell *cell = [[UITableViewCell alloc] init];
the above code can solve your issue, but this is not good if there are a large number of cells. In that case you should use reusable cells.
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
else
{
UILabel *titleLbl = (UILabel *)[cell viewWithTag:1];
[titleLbl removeFromSuperview];
}
if (indexPath.row == 2)
{
pendingListNumberLabel = [[UILabel alloc] init];
pendingListNumberLabel.font = [UIFont fontWithName:#"Helvetica" size:14];
pendingListNumberLabel.textAlignment = UITextAlignmentLeft ;
pendingListNumberLabel.textColor = [UIColor orangeColor];
pendingListNumberLabel.tag = 1;// Write here.....................................
pendingListNumberLabel.frame = CGRectMake(240, 6, 110, 30);
[pendingListNumberLabel setBackgroundColor:[UIColor clearColor]];
[cell addSubview:pendingListNumberLabel];
}
cell.textLabel.text = [affiliationsArray objectAtIndex:indexPath.row];
[cell setAccessoryType:UITableViewCellAccessoryDisclosureIndicator];
return cell;
I think it will be helpful to you.
You can fix the problem of label mixing just by omitting the if (cell == nil) checking. Use the bellow code:
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
//if (cell == nil) // Need not this checking
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
Use the cell allocation directly as above :)
In your tableView:cellForRowAtIndexPath: method, first get the selected row like this
NSUInteger row = [indexPath row];
then use "row" to grab your data from an NSArray that corresponds to the tableView rows and assign it to your cell's textLabel's text property like this
NSString *string = [NSString stringWithFormat:#"%#",self.allItems objectAtIndex:row];
cell.textLabel.text = string;
return cell;
And of course you would have already created a UITableViewCell with a Reuse identifier like this :
static NSString *TableIdentifier = #"TableIdentifier";
UITableViewCell *cell = [tv dequeueReusableCellWithIdentifier:TableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleValue1
reuseIdentifier:TableIdentifier];
}
The key is using an array that corresponds exactly to your table rows. (ie - item 0 in the Array corresponds to row 0 in the tableView) then you can't go wrong.
For solve this issue you can put cell=nil; before checking cell== nil condition or create cell every time. It will work but it is not good Habit.
This also work for Text
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
cell = nil;
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
if (indexPath.row == 2)
{
pendingListNumberLabel = [[UILabel alloc] init];
pendingListNumberLabel.font = [UIFont fontWithName:#"Helvetica" size:14];
pendingListNumberLabel.textAlignment = UITextAlignmentLeft ;
pendingListNumberLabel.textColor = [UIColor orangeColor];
pendingListNumberLabel.frame = CGRectMake(240, 6, 110, 30);
[pendingListNumberLabel setBackgroundColor:[UIColor clearColor]];
[cell addSubview:pendingListNumberLabel];
}
cell.textLabel.text = [affiliationsArray objectAtIndex:indexPath.row];
[cell setAccessoryType:UITableViewCellAccessoryDisclosureIndicator];
return cell;
I have a tableview controller, with a label and a switch displayed in a row. To add elements like this, I am using the approach below(as is recommended to avoid the data in the row getting mixed up).
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
....
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
UISwitch* testSwitch = [[UISwitch alloc] initWithFrame:
CGRectMake(200,
0.5 * (cell.frame.size.height-30 ),
30, 30)];
testSwitch.tag = 2;//If this is a dynamic value, it only works for 14 rows
[cell.contentView addSubview:testSwitch];
[testSwitch release];
}
UISwitch *switch1 = (UISwitch*) [cell viewWithTag:2];
//cannot set a dynamic value here
switch1.on = [<array value> boolValue];
I need the tag to be a dynamic value depending on the data on the current row so that I can add an action method when the value changes, and make according model related changes.
However, since the tag is set in the first section, if I make that dynamic, only 14 values(which are the number of rows displayed on the screen) are set.
What do I do so that I can store a dynamic value on all the rows of this switch for its target action method to retrieve?
Thanks..
--Edit---
Changed code to
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
UISwitch* testSwitch = [[UISwitch alloc] initWithFrame:
CGRectMake(200,
0.5 * (cell.frame.size.height-30 ),
30, 30)];
testSwitch.tag = indexPath.row;//If this is a dynamic value, it only works for 14 rows
[cell.contentView addSubview:testSwitch];
[testSwitch release];
}
UISwitch *switch1 = (UISwitch*) [cell viewWithTag:indexPath.row];
switch1.on = someboolValue; //CRASH as switch1 points to UItablviewcell and not UISwitch.
testSwitch.tag=[indexPath row]
assuming you only have one section.
ok, it now sounds like you are having a problem that
[cell viewWithTag:indexPath.row]
is locating the cell and not it's subview so assuming you only have one uiswitch subview:
for (UIView *v in [cell subviews]) {
if ([v isKindOfClass:[UISwitch class]]) {
v.on = someboolValue;
}
}
I am stuck with this. I will explain with my code
- (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];
}
NSNumber *cellno=[NSNumber numberWithUnsignedInteger:indexPath.row];
imgView = [[UIImageView alloc] initWithFrame:CGRectMake(240, 13, 15,18)];
imgView.image=[UIImage imageNamed:#"lock.png"];
tickView = [[UIImageView alloc] initWithFrame:CGRectMake(200, 13, 15,18)];
tickView.image=[UIImage imageNamed:#"tick.png"];
switch (indexPath.row) {
case 0:
cell.textLabel.text=#"apples";
if ([appDelegate.connected containsObject:cellno]) { //condition
[cell.contentView addSubview:tickView];
}else{
[cell.contentView addSubview:imgView];
}
break;
}
cell.accessoryType=UITableViewCellAccessoryDetailDisclosureButton;
return cell;
}
During the first time the tableview is loaded the 'imgView' subview is added to the cells content view and the after some operation the 'if' condition is satisfied and the 'tickView' is added.
The problem is, the oldview is not hidden or removed and hence both the images appear.
Help would be greatly appreciated
Instead of creating your imgView and tickView views in the cellForRowAtIndexPath method, create them when you create the cell and reuse them with the cell. Then you can do this:
...
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
imgView = [[UIImageView alloc] initWithFrame:CGRectMake(240, 13, 15,18)];
imgView.image=[UIImage imageNamed:#"lock.png"];
tickView = [[UIImageView alloc] initWithFrame:CGRectMake(200, 13, 15,18)];
tickView.image=[UIImage imageNamed:#"tick.png"];
}
...
if ([appDelegate.connected containsObject:cellno]) { //condition
[imgView removeFromSuperview];
[cell.contentView addSubview:tickView];
}else{
[tickView removeFromSuperview];
[cell.contentView addSubview:imgView];
}
I am adding an activity indicator in each row of table. The issue is every time I scroll it get added again in cell overwriting the previous one. Please let me know which is best way to add control in tableview cell.
UIActivityIndicatorView *a;
// 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];
}
a = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
[a startAnimating];
[cell.contentView addSubview:a];
// Configure the cell.
return cell;
}
Add indicator to a cell only when you create it, then you can access it using its tag property:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
a = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
a.tag = 10;
[cell.contentView addSubview:a];
}
UIActivityIndicatorView* aView = [cell.contentView viewWithTag:10];
[aView startAnimating];