Iphone - cellForRowAtIndexPath behaving weirdly when scrolling down and up - iphone

I would like to get a TableView with different elements on the accessoryView, depending on the section. Everything works fine, but when I scroll down to section 2 and then scroll up again to section 0, the rows in section 0 have switchers. Could you please help me finding out what I'm doing wrong?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSInteger sectionIndex = [indexPath section];
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
NSDictionary *dictionary;
NSArray *array;
NSString *cellValue;
UISwitch *switchView;
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
switch (sectionIndex) {
case 0:
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
break;
case 1:
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
break;
case 2:
switchView = [[UISwitch alloc] initWithFrame:CGRectZero];
cell.accessoryView = switchView;
[switchView setOn:NO animated:NO];
[switchView addTarget:self action:#selector(switchChanged:) forControlEvents:UIControlEventValueChanged];
[switchView release];
break;
default:
break;
}
dictionary = [searchArray objectAtIndex:indexPath.section];
array = [dictionary objectForKey:#"advancedSearch"];
cellValue = [array objectAtIndex:indexPath.row];
cell.textLabel.text = cellValue;
return cell;
}

You are dequeuing your table cells (cf. -[UITableView dequeueReusableCellWithIdentifier:]), which means that you are receiving table views that have already been allocated and possibly pre-populated with stale data. In case 0: and case 1: of your switch statement, you should add the following line:
cell.accessoryView = nil;

Use different cell identifiers for different cells.
Something like:
NSString *CellIdentifier;
switch ([indexPath row]) {
case 0: CellIdentifier = #"cell0";
break;
case 1: CellIdentifier = #"cell1";
break;
default: CellIdentifier = #"cell";
break;
}
Otherwise Xcode thinks that all your cells have the same look and tries to reuse them while you scroll.

The problem is from dequeueReusableCellWithIdentifier you must use different identifiers for different cells because you reuse them.

Related

How can i dynamically change the UISwitch of UITableView?

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;
}
}

cell.textLabel not getting resized

I'm trying to create a Settings for our app. I'm not sure what is happening here. I have a UITableViewSyleGrouped and in each section of the table, there is 1 row. For my particular row, it shows the person's name. If you click on it, then it pushes to a new tableView that has the list of people to choose from, then when you pop back, the label gets updated, but the label is truncated when I go from a smaller name to a bigger name. I'm trying to create a Settings for our app. Some of the fields look like:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSUInteger row = [indexPath row];
if (tableView == _settingsTableView) {
UITableViewCell *cell = nil;
NSNumber *aSection = [_tableArray objectAtIndex:indexPath.section];
if ([aSection integerValue] == SOUNDS)
{
cell = [tableView dequeueReusableCellWithIdentifier:#"SwitchCell"];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"SwitchCell"] autorelease];
}
cell.textLabel.text = #"Sounds";
cell.selectionStyle = UITableViewCellSelectionStyleNone;
UISwitch *switchView = [[UISwitch alloc] initWithFrame:CGRectZero];
cell.accessoryView = switchView;
[switchView setOn:[[Settings sharedInstance] playSounds] animated:NO]; // initialize value from Settings
[switchView addTarget:self action:#selector(switchChanged:) forControlEvents:UIControlEventValueChanged];
[switchView release];
}
else if ([aSection integerValue] == PERSON) {
cell = [tableView dequeueReusableCellWithIdentifier:#"PersonCell"];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:#"PersonCell"] autorelease];
}
Person *p = [_personArray objectAtIndex:row];
cell.textLabel.text = [NSString stringWithFormat:#"%# %#", p.firstName, p.lastName];
NSLog(#"cL: %#", NSStringFromCGRect(cell.textLabel.frame));
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
return cell;
}
My PERSON section gives the user the ability to change People. That code in didSelectRowAtIndexPath is
else {
Person *p = [_personArray objectAtIndex:row];
NSUInteger oldRow = [_lastIndexPath row];
if (oldRow != row) {
dmgr.currentPerson = p;
// Put checkmark on newly selected cell
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.accessoryType = UITableViewCellAccessoryCheckmark;
// Remove checkmark on old cell
UITableViewCell *oldCell = [tableView cellForRowAtIndexPath:_lastIndexPath];
oldCell.accessoryType = UITableViewCellAccessoryNone;
[_settingsTableView deselectRowAtIndexPath:_lastIndexPath animated:YES];
self.LastIndexPath = indexPath;
// Update the cell
NSIndexPath *path = [NSIndexPath indexPathForRow:0 PERSON];
UITableViewCell *theCell = [_settingsTableView path];
theCell.textLabel.text = [NSString stringWithFormat:#"%# %#", p.firstName, p.lastName];
[theCell setNeedsDisplay];
NSLog(#"ceLL: %#", NSStringFromCGRect(theCell.textLabel.frame));
}
}
What happens is the label is truncated until I click on the label. (e.g. John D... instead of John Doe). Why does the label not get updated?
I tried looking at the frames, and I'm not sure if that has something to do with it or not. My output is:
cL: {{0, 0}, {0, 0}}
ceLL: {{10, 11}, {76, 21}}
The textLabel field of a UITableViewCell is a regular UILabel. You can set this property to cause it to scale down the text to fit:
theCell.textLabel.adjustsFontSizeToFitWidth = YES;
You can also set a minimum font size
theCell.textLabel.minimumFontSize = whatever
Take a look at the Documentation on UILabel it will help you a lot.

UITableView selected cell not displaying text when started

following code sets cell texts and add a checkMark only to last selected. Always only one cell check marked and works properly excepting when it is displayed for first time. So, text is not showed for that cell (only that one) until you press any other cell. For example, if cellPos = 4 when viewDidLoad, that cell will not contain text.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString* cellIdentifier = #"cellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if(cell == nil)
{
cell = (UITableViewCell*) [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellIdentifier];
}
cell.accessoryType = UITableViewCellAccessoryNone;
if (indexPath.section == 0) {
if(indexPath.row == cellPos)
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
cell.selected = YES;
}
else
{
cell.accessoryType = UITableViewCellAccessoryNone;
cell.selected = NO;
}
switch (indexPath.row) {
case 0:
cell.textLabel.text = #"English";
cell.imageView.image = [UIImage imageNamed:#"english.png"];
break;
case 1:
cell.textLabel.text = #"Español";
cell.imageView.image = [UIImage imageNamed:#"spanish.png"];
break;
case 2:
cell.textLabel.text = #"Deutsch";
cell.imageView.image = [UIImage imageNamed:#"germany.png"];
break;
case 3:
cell.textLabel.text = #"Français";
cell.imageView.image = [UIImage imageNamed:#"french.png"];
break;
case 4:
cell.textLabel.text = #"Italiano";
cell.imageView.image = [UIImage imageNamed:#"italian.png"];
break;
default:
break;}
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
cellPos = indexPath.row;
[tableView reloadData];
}
Have you tried moving the if-else block after the switch block? This way you set the text of the cell before you set the cell to selected. The fact that it only occurs the first time suggests to me that it may be an order of operations problem.

UITableView cell/data disappear

I have a segmented tableView that loads all the data in all the cells of all the sections.
There is a textField in each cell.
The tableview doesn't fit the iPad screen completely, and I can't access all the invisible cells in order to read/save data. And when I make changes in "textField", then scroll up, the scroll down, all the changes are gone.
I need to load all the cells, even invisible once, to be able to access them.
I am sorry, I just started working with tables a few days ago...
I think that this problem has something to do with reusable cells, but not sure how to resolve it.
Looking for your help, please.
initialization:
- (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] autorelease];
UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 400, 30)] ;
textField.enabled = NO;
cell.accessoryView = textField;
[textField release];
}
UITextField *textField = (UITextField*)cell.accessoryView;
if(indexPath.section == 0)
cell.textLabel.text = [idenInfo objectAtIndex:indexPath.row];
else if (indexPath.section == 1)
cell.textLabel.text = [prodInfo objectAtIndex:indexPath.row];
else if (indexPath.section == 2)
cell.textLabel.text = [visInfo objectAtIndex:indexPath.row];
if(indexPath.section == 0)
textField.text = [idenInfoRez objectAtIndex:indexPath.row];
else if (indexPath.section == 1)
textField.text = [prodInfoRez objectAtIndex:indexPath.row];
else if (indexPath.section == 2)
textField.text = [visInfoRez objectAtIndex:indexPath.row];
textField = nil;
return cell;
}
First of all : you don't have to load all the cells including the invisible ones. That's the whole point of the UITableView and MVC Pattern : separate your views from your data.
What you'll want to do is update your Data source (that is idenInfoRez, prodInfoRez and vizInfoRez in your case) when the user has changed a value inside a textField. So you'll have to set your UIViewController as the delegate of each textfield and update the values as the user types in.
[UIView beginAnimations:#"ShiftUp" context:nil];
[UIView setAnimationDuration:0.0001];
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSInteger section = [indexPath section];
static NSString *CellIdentifier = #"Cell";
CustomCell *cell = (CustomCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
[[NSBundle mainBundle] loadNibNamed:#"CustomCell" owner:self options:nil];
cell = objCustCell;
cell.selectionStyle = UITableViewCellSelectionStyleNone ;
}
if (section == 0) {
switch (indexPath.row) {
case 0:{
cell.lable.text = #"Date of Birth";
cell.field.placeholder = #"Birth Name";
break;
}
case 1:{
cell.lable.text = #"Enter Your Name";
cell.field.placeholder = #"Full Name";
break;
}
default:
break;
}
}
[UIView beginAnimations:#"ShiftUp" context:nil];
[UIView setAnimationDuration:0.0001];
// [UIView beginAnimations: #"ShiftUp" context:nil];
NSLog(#"Load cellfor raw at index ");
// Configure the cell.
return cell;
}
Note : UIView animation will not allow text field to move away data or any UIcontroller will remain same in its old state !!
Don't Commit animation otherwise it will not working !!
What you can do is this: in the Editing Changed event of each of the TextField store the value contain in the text field in an NSMutableArray whose number equal the number of cells. i.e.
-(IBAction) EditingChanged: (id) sender
{
UITextField *txt =(UITextField*)sender;
NSString* str =[[NSString alloc] initWithString: txt.text];
//get current text string
NSInteger path =[tableView indexPathForSelectedRow].row;
// get currently selected row, this could be a bit different depending on the number of sections
[yourMutableArray insertObject: str atIndex: path];
[str release]
}
You can then populate the TextField with the values from the NSMutableArray anytime the cells are recreated i.e.
(UITableViewCell *) tableView: (UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*) indexPath
{
...... // create the cells here and use viewTag to get the textFields
textField.text= [yourMutableArray objectAtIndex:indexPath.row];
//This may be a bit different depending on the number of sections.
}
Also, note that it might be advisable to initialize yourMutable array to the capacity of the number of cells.
I am sorry if the codes are not well formatted as this is my first post on stackoverflow - also there might be some typos in the code. Hope this helps someone.
every time we allocate the cell to different data,the data will not reloading the cell,every time the data override previous data, before allocate the cell to clear cell,
like as cell=nil
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
cell=nil;
//it clear data in the cell
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 400, 30)] ;
textField.enabled = NO;
cell.accessoryView = textField;
[textField release];
}
You're right, the problem is the cells will be reused. There are two solutions to the problem, the quick and dirty one would be to not use reusable cells:
Remove this:
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
And just leave this:
UITableViewCell * cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 400, 30)] ;
textField.enabled = NO;
cell.accessoryView = textField;
[textField release];
That should be ok, if you have only a small number of cells in your tableview (about fewer than 50).
The better solution would be to leave cell reuse on, and fill their textfields as they are requested. The approach differs from app to app, but you basically never should access the cells directly, and store the data of the cell somewhere else, e.g. an NSArray of NSStrings. You could then manipulate the NSArray. Your cellForRowAtIndexPath method would look something like this:
textField.text = [arrData objectAtIndex:indexPath.row];

iphone keyboard won't appear

I can click on the field, and it momentarily turns blue, but those events plus makeFirstResponder together do not cause the keyboard to show on a UITextField.
Plain vanilla code follows; I include it so others can discern what is NOT there and therefore what, presumably, with solve the problem.
I put in leading spaces to format this question more like code, but the parser seems to have stripped them, thus leaving left-justified code. Sorry!
UITextFieldDelete, check:
#interface RevenueViewController : UIViewController <UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate> {
UILabel *sgmntdControlLabel;
UISegmentedControl *sgmntdControl;
UITableView *theTableView;
}
#property(nonatomic,retain) UISegmentedControl *sgmntdControl;
#property(nonatomic,retain) UILabel *sgmntdControlLabel;
Delegate and source, check.
-(void)loadView;
// code
CGRect frameTable = CGRectMake(10,0,300,260);
theTableView = [[UITableView alloc] initWithFrame:frameTable style:UITableViewStyleGrouped];
[theTableView setDelegate:self];
[theTableView setDataSource:self];
// code
[self.view addSubview:theTableView];
[super viewDidLoad];
}
Insert UITextField to cell, check.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSInteger sectionNmbr = [indexPath section];
NSInteger rowNmbr = [indexPath row];
NSLog(#"cellForRowAtIndexPath=%d, %d",sectionNmbr,rowNmbr);
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
// Configure the cell.
switch (sectionNmbr) {
case 0:
if (rowNmbr == 0) {
cell.tag = 1;
cell.textLabel.text = #"Label for sctn 0, row 0";
UITextField *tf = [[UITextField alloc] init];
tf.keyboardType = UIKeyboardTypeNumberPad;
tf.returnKeyType = UIReturnKeyDone;
tf.delegate = self;
[cell.contentView addSubview:tf];
}
if (rowNmbr == 1) {
cell.tag = 2;
cell.textLabel.text = #"Label for sctn 0, row 1";
cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
}
break;
}
}
Successfully end up where we want to be (?), no check, (and no keyboard!):
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"didSelectRowAtIndexPath");
switch (indexPath.section) {
case 0:
NSLog(#" section=0, rowNmbr=%d",indexPath.row);
switch (indexPath.row) {
case 0:
UITableViewCell *cellSelected = [tableView cellForRowAtIndexPath: indexPath];
UITextField *textField = [[cellSelected.contentView subviews] objectAtIndex: 0];
[ textField setEnabled: YES ];
[textField becomeFirstResponder];
// here is where keyboard will appear?
[tableView deselectRowAtIndexPath: indexPath animated: NO];
break;
case 1:
// code
break;
default:
break;
}
break;
case 1:
// code
break;
default:
// handle otherwise un-handled exception
break;
}
}
Thank you for your insights!
are you sure that
[[cellSelected.contentView subviews] objectAtIndex: 0];
is returning what you expect it to? Try checking it with the debugger