Strange behavior of UITableView when editing - ios

I have a editable UITableView (with adding and deleting elements) in my application.
It working strange.
While I have only one or two lines in it, everything is working perfect.
But if I add more items, I have an exception '*** -[__NSArrayI objectAtIndex:]: index 3 beyond bounds [0 .. 2]'
I was unable to put breakpoints and handle it.
Maybe, somebody can help me?
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
int plusRow = 0;
if ([[self tableView] isEditing]) plusRow = 1;
return [typeList count] + plusRow;
}
- (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];
}
if ([indexPath row] > ([typeList count]-1)) {
NSString * appendCellDescription = #"";
if ([[self tableView] isEditing]) appendCellDescription = #"Add";
[[cell textLabel] setText:appendCellDescription];
} else {
NSLog(#"Accessing array: %d", [indexPath row]);
[[cell textLabel] setText:[[typeList getObject:[indexPath row]] description]];
}
return cell;
}
#pragma mark - Editing table view
- (UITableViewCellEditingStyle) tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath{
NSLog(#"Row: %d, count: %d", [indexPath row], [typeList count]);
if (indexPath.row > [typeList count]-1) {
return UITableViewCellEditingStyleInsert;
} else {
return UITableViewCellEditingStyleDelete;
}
}
- (IBAction)btnModifyClick:(id)sender{
if ([[self tableView] isEditing]) {
[[self tableView] setEditing:FALSE animated:YES];
[[self tableView] reloadData];
} else {
[[self tableView] setEditing:TRUE animated:YES];
[[self tableView] reloadData];
}
}

Generally This Error describe, When mistake in Array Index such like you array have 3 item and you tring to get/abstract 4th item from Array, at that time this type of Error generate.
Best way to find error use BreakPint. and Debug your project line by line. and find where your app. crush ?? i am sure that it cruse Around any array.
EDIT:
I thinks mistake in array name typeList check it in cellForRowAtIndexPath method with BreakPoint.

numberOfRowsInSectionis returning higher number of items count than the one fetched in cellForRowAtIndexPath datasource method, try debugging the returned value:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
int plusRow = 0;
if ([[self tableView] isEditing]) plusRow = 1;
NSLog(#"%i",[typeList count] + plusRow);
return [typeList count] + plusRow;
}

I have found a way, to solve my problem.
I just changed "Sections" to 0 in XCode properties in storyboard TableView.
And now working ok.
Thanks to everyone to response!

Related

Section header is repeating when UITextView size is increased in UITableView Cells

I have been playing around with this problem since a day and I have tried many ways to resolve this issue but have not yet succeeded. I have a tableview with 3 custom cells and I have added section header for last two sections. Here is the screen shot.
which shows last section header is repeating when I enter text in the TextView. My TextView is editable and I have disabled the scrolling to adjust the size of the textview as per the text size.
Here is the code.
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 3;
}
-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
NSArray *titleArray = #[#"",#"About You",#"Gender"];
NSString *titleHeading = [titleArray objectAtIndex:section];
return titleHeading;
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
return section == 0 ? CGFLOAT_MIN : 35;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *identifier = [NSString stringWithFormat:#"cell%ld,%ld",indexPath.section, indexPath.row];
id cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (indexPath.section == 1)
{
ProfileAboutCell *cellObj = [tableView dequeueReusableCellWithIdentifier:identifier];
if (!cellObj)
{
[_tableView registerNib:[UINib nibWithNibName:#"ProfileAboutCell" bundle:nil] forCellReuseIdentifier:identifier];
cellObj = [tableView dequeueReusableCellWithIdentifier:identifier];
}
cellObj.selectionStyle = UITableViewCellSelectionStyleNone;
//[cellObj.txtAboutYou layoutIfNeeded];
cellObj.txtAboutYou.delegate = self;
cellObj.lbl = [[UILabel alloc] initWithFrame:CGRectMake(5.0, 0.0,cellObj.txtAboutYou.frame.size.width - 10.0, 34.0)];
cellObj.lbl.text = #"About You";
[cellObj.lbl setBackgroundColor:[UIColor clearColor]];
[cellObj.lbl setTextColor:[UIColor lightGrayColor]];
[cellObj.txtAboutYou addSubview:cellObj.lbl];
// [cellObj.txtAboutYou setText:[kUserDefaults valueForKey:kAbout]];
//[cellObj.txtAboutYou sizeToFit];
cell = cellObj;
}
return cell;
}
TextView Delegate Method.
-(void)textViewDidChange:(UITextView *)textView
{
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:1];
ProfileAboutCell *cell = [_tableView cellForRowAtIndexPath:indexPath];
if(![textView hasText]) {
cell.lbl.hidden = NO;
}
else{
cell.lbl.hidden = YES;
}
NSInteger len = textView.text.length;
cell.lblChar.text=[NSString stringWithFormat:#"%li",500-len];
[_tableView beginUpdates];
[_tableView endUpdates];
}
I have tried this #SO solution but no help. Any help would be much appreciated in this direction. Thanks in advance.
From my understanding of the issue you can solve it by set the sections headers in "titleForHeaderInSection" the same way you set the cells in "cellForRowAtIndexPath" for each section,
this way sections that you don't set Headers for will not cause any issue.
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
NSArray *titleArray = #[#"",#"About You",#"Gender"];
if (indexPath.section == 1) {
NSString *titleHeading = [titleArray objectAtIndex:1];
return titleHeading;
}
if (indexPath.section == 2) {
NSString *titleHeading = [titleArray objectAtIndex:2];
return titleHeading;
}
}

Nested UITableView in a UITableView Causes NsRangeException with Index Out of Bound in iOS

I have an iOS 7/8 application. In a view I have a static UITableView with given number of cells - 17 in my case.
One of the cells contains another UITableView with dynamic cells. In the case described they are 20.
Because of the difference in the number of cells (+3) I get
NSRangeException', reason: '*** -[__NSArrayI objectAtIndex:]: index 17 beyond bounds [0 .. 16]
exception when I set the 18th cell in the dynamic view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (tableView == _dynamicTableView) {
NSLog(#"%lu", (unsigned long)[[_filter types] count]);
return [[_filter types] count];
} else {
return 17;
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if (tableView == _dynamicTableView) {
static NSString *cellIdentifier = #"type";
TypeTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[TypeTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:cellIdentifier];
}
NSArray *types = [_filter types];
Type *type = [types objectAtIndex:[indexPath row]];
[cell.nameLabel setText:[type name]];
return cell;
} else {
return [super tableView:tableView cellForRowAtIndexPath:indexPath];
}
}
When I go to the Storyboard and increase the number of static cells to something like 30 everything works fine. tableView:numberOfRowsInSection... method removes the unused cells - only 17 static and 20 dynamic cells are shown.
I am aware that the source of my problem is having two UITableView-s in one controller and the large amount of 'if'-s.
Try to check with tags, you can give different tags to each table and return number of cells according to that.
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (tableView.tag == 1) // Tag for _dynamicTableView
{
return [[_filter types] count];
}
else {
return 17;
}
}

UITableView mixing up section items

I'm trying to open 2 different Viewcontrollers from the first 2 sections of my tableview and some URL's from the other 4 sections. The first section doesn't do anything and the others are mixing up, and the sections with which should open the URL's don't work. Thanks for your responses.
#implementation SettingsViewController {
NSArray * sectionheaderArr;
}
- (void)viewDidLoad
{
sectionheaderArr = [[NSArray alloc]initWithObjects:NSLocalizedString(#"fb",
nil),NSLocalizedString(#"recommend", nil),NSLocalizedString(#"feedback",
nil),NSLocalizedString(#"rate", nil),NSLocalizedString(#"language",
nil),NSLocalizedString(#"all_providers", nil), nil];
[super viewDidLoad];
// Do any additional setup after loading the view.
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:
(NSInteger)section{
return sectionheaderArr.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:
(NSIndexPath *)indexPath {
static NSString *CellIdentifier =#"Cell";
int row = indexPath.row;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
}
cell.textLabel.text = [sectionheaderArr objectAtIndex:row];
return cell;
}
-(void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath
*)indexPath{
if (indexPath.section == 0) {
if (indexPath.row ==0){
[self performSegueWithIdentifier:#"openFacebook" sender:self];
}
}
if (indexPath.section ==1) {
if (indexPath.row ==1) {
[self performSegueWithIdentifier:#"opentest" sender:self];
}
}
if (indexPath.section==2) {
if (indexPath.row ==2) {
UIApplication*app1 = [UIApplication sharedApplication];
NSString*path = #"http://www.google.de";
NSURL*myurl = [NSURL URLWithString:path];
[app1 openURL:myurl];
}
}
}
Here it is the answer to your question:
#implementation SettingsViewController {
NSArray * sectionheaderArr;
}
- (void)viewDidLoad
{
sectionheaderArr = [[NSArray alloc]initWithObjects:NSLocalizedString(#"fb",
nil),NSLocalizedString(#"recommend", nil),NSLocalizedString(#"feedback",
nil),NSLocalizedString(#"rate", nil),NSLocalizedString(#"language",
nil),NSLocalizedString(#"all_providers", nil), nil];
[super viewDidLoad];
// Do any additional setup after loading the view.
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:
(NSInteger)section{
return sectionheaderArr.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:
(NSIndexPath *)indexPath {
static NSString *CellIdentifier =#"Cell";
int row = indexPath.row;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
}
cell.textLabel.text = [sectionheaderArr objectAtIndex:row];
return cell;
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath
*)indexPath{
if (indexPath.row ==0){
[self performSegueWithIdentifier:#"openFacebook" sender:self];
}
if (indexPath.row ==1) {
[self performSegueWithIdentifier:#"opentest" sender:self];
}
if (indexPath.row ==2) {
UIApplication*app1 = [UIApplication sharedApplication];
NSString*path = #"http://www.google.de";
NSURL*myurl = [NSURL URLWithString:path];
[app1 openURL:myurl];
}
}
Please note that I've also changed the "didSelectRowAtIndexPath" method as you were implementing "didDeselectRowAtIndexPath" so it won't work until you DEselect your cell.
Thanks also #iphonic for his explanation.
The datasource method for UITableView
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
is used to define number of sections for UITableView, and each section has different rows starts from 0,1,2.....
In your case you are trying with different indexPath.section where as it should use only 1 section, and for multiple sections there should be multiple datasource for the sections to be defined.
As you already got the solution, I just want to clear the concept of indexPaths rows and sections.
Thanks.

TableView Crashes when Trying to Insert

My app crashes when I try to insert a new entry. I received this error previously, and I was not able to fix it. Can anyone help?
Here is my error displayed in the console:
2013-09-14 15:41:00.370 Probation App[9919:907] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 2 beyond bounds [0 .. 1]'
*** First throw call stack:
(0x31fe82a3 0x39ccc97f 0x31f33b75 0xd7b81 0x33eb228d 0x33f34f81 0x328f6277 0x31fbd5df 0x31fbd291 0x31fbbf01 0x31f2eebd 0x31f2ed49 0x35af62eb 0x33e44301 0x542fd 0x3a103b20)
libc++abi.dylib: terminate called throwing an exception
and here is my code:
#pragma mark UITableViewDataSource Methods
- (UITableViewCell *)tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tv dequeueReusableCellWithIdentifier:#"cell"];
if( nil == cell)
{
cell = [[UITableViewCell alloc] initWithStyle: UITableViewCellStyleSubtitle reuseIdentifier:#"cell"];
}
if (indexPath.row <self.probationers.count)
{
Probationer *thisProbationer = [self.probationers objectAtIndex:indexPath.row];
cell.textLabel.text = thisProbationer.probationerName;
cell.detailTextLabel.text = thisProbationer.probationerID;
}
else
{
cell.textLabel.text = #"Add Probationer";
cell.textLabel.textColor = [UIColor lightGrayColor];
cell.editingAccessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
return cell;
}
-(NSInteger)tableView: (UITableView *)tv numberOfRowsInSection:(NSInteger)section
{
NSInteger count = self.probationers.count;
if(self.editing)
{
count = count +1;
}
return count;
//return [self.probationers count];
}
//Deleting an Entry
-(void) tableView:(UITableView *)tv commitEditingStyle:(UITableViewCellEditingStyle) editing forRowAtIndexPath: (NSIndexPath *) indexPath
{
if (editing == UITableViewCellEditingStyleDelete)
{
[self.probationers removeObjectAtIndex:indexPath.row];
[tv deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationLeft];
}
}
#pragma mark UITableViewDelegate Methods
-(void)tableView:(UITableView *)tv didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
Probationer *chosenProbationer = [self.probationers objectAtIndex:indexPath.row];
ProbationerController *detailedViewController = [[ProbationerController alloc]init];
detailedViewController.delegate = self;
detailedViewController.currentProbationer = chosenProbationer;
if (indexPath.row <self.probationers.count && !self.editing)
{
[self.navigationController pushViewController:detailedViewController animated:YES];
}
if (indexPath.row == self.probationers.count && self.editing)
{
AddProbationerController *addProbationer = [[AddProbationerController alloc] init];
[self.navigationController pushViewController:addProbationer animated:YES];
}
[tv deselectRowAtIndexPath:indexPath animated:YES];
//selectedIndexPath = indexPath;
//[self.navigationController pushViewController:detailedViewController animated:YES];
}
The number of rows should always come from the data source (the bit you have commented out //return [self.probationers count];). Don't try to just add to the number. Add to the data source and then refresh the table view.
The stack trace say it all. Its definitely because of you self.probationers array.
Why don't you NSLog the number of items in the array and the number of rows/sections in your tableview.
When you are trying to associate indexPath.row and self.probationers you have to make sure they match with number of elements.
Also follow the basics while accessing elements of array(as they are much prone to get exceptions)
Nil check for the accessing array.
Know the exact count of the array, by logging.
Make sure you are trying to access any objects more than the available array indexes.

Add and delete rows dynamically in multiple sections

I have searched and search and i just canĀ“t seem to find an answer to my problem. I have a dynamic tableview with 3 rows (each row is a section) and a edit button at the top right of the tableview. each time the user taps edit it has to be possible to add or delete a row. Everything works except the part when the + button is taped to ad a new row. This is my code:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 3;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
int count = [myarray count];
if (myarray != nil) count ++;
return count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
id cell;
switch(indexPath.section) {
case 0:
if(indexPath.row==0) {
static NSString *cellType1 = #"cellType1";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellType1];
return cell;
}
break;
case 1:
if(indexPath.row==0) {
static NSString *cellType2= #"cellType2";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellType2];
return cell;
}
break;
case 2:
if(indexPath.row==0) {
static NSString *cellType3= #"cellType3";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellType3];
return cell;
}
break;
default:
break;
}
return cell;
}
- (IBAction) EditTable:(id)sender
{
if(self.editing)
{
[super setEditing:NO animated:NO];
[Table setEditing:NO animated:NO];
[Table reloadData];
[self.navigationItem.rightBarButtonItem setTitle:#"Edit"];
[self.navigationItem.rightBarButtonItem setStyle:UIBarButtonItemStylePlain];
}
else
{
[super setEditing:YES animated:YES];
[Table setEditing:YES animated:YES];
[Table reloadData];
[self.navigationItem.rightBarButtonItem setTitle:#"Ok"];
[self.navigationItem.rightBarButtonItem setStyle:UIBarButtonItemStyleDone];
}
}
- (UITableViewCellEditingStyle)tableView:(UITableView *)aTableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (self.editing == NO || !indexPath) return UITableViewCellEditingStyleNone;
if (self.editing && indexPath.row == ([myarray count]))
{
return UITableViewCellEditingStyleInsert;
} else
{
return UITableViewCellEditingStyleDelete;
}
return UITableViewCellEditingStyleNone;
}
- (void)tableView:(UITableView *)TableView commitEditingStyle: (UITableViewCellEditingStyle)editingStyle
forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete)
{
[myarray removeObjectAtIndex:indexPath.row];
[Table reloadData];
} else if (editingStyle == UITableViewCellEditingStyleInsert)
{
switch(indexPath.section) {
case 0:
if(indexPath.row==0) {
static NSString *cellType1 = #"cellType1";
UITableViewCell *cell = [TableView dequeueReusableCellWithIdentifier:cellType1];
[arry insertObject:cell atIndex:[myarray count]];
[Table reloadData];
}
break;
case 1:
if(indexPath.row==0) {
static NSString *cellType2= #"cellType2";
UITableViewCell *cell = [TableView dequeueReusableCellWithIdentifier:cellType2];
[arry insertObject:cell atIndex:[myarray count]];
}
break;
case 2:
if(indexPath.row==0) {
static NSString *cellType3= #"cellType3";
UITableViewCell *cell = [TableView dequeueReusableCellWithIdentifier:cellType3];
[arry insertObject:cell atIndex:[myarray count]];
}
break;
default:
break;
}
}
As i said before, everything works until i press the + button (that appears on the left side when the edit button is pressed) to add a new row. Then it shows an error: 'UITableView dataSource must return a cell from tableView:cellForRowAtIndexPath.
What am i doing wrong? any help would be most appreciated.
First off, you never actually +alloc or -init a cell, so -cellForRowAtIndexPath: is most likely returning nil (-dequeueReusableCellWithIdentifier: doesn't cut it).
Second off, comparing indexPath.row to [myarray count] will never be true, because arrays and tables may be zero-based, but their counts aren't.

Resources