Assertion failure in UITableView _endCellAnimationsWithContext [duplicate] - ios

This question already has answers here:
'Invalid update: invalid number of rows in section 0
(4 answers)
Closed 8 years ago.
i have a problem like this :
i am loading some data to a table view after select a date from a date picker.the data which i want to load is loaded to a variable inside the didSelectRowAtIndexPath method. when the table is checking for the number of rows it checks the count of that variable is grater than zero and so on..
my problem is this : when i load the data to the variable by calling a web service [using AFNetworking library] it is works nicely. but when i get the data directly [i have implemented a method to call web service before the table load, and holds the data] by a method it is giving this error
Assertion failure in -[UITableView _endCellAnimationsWithContext:], /SourceCache/UIKit_Sim/UIKit-2903.23/UITableView.m:1330
2014-05-02 17:25:12.665 varrdle_v2[1428:a0b] * Terminating app due to uncaught exception
NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (3) must be equal to the number of rows contained in that section before the update (2), plus or minus the number of rows inserted or deleted from that section (0 inserted, 1 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).
* First throw call stack:
i cant figure out why that happens. the only different is the way i taken the data.
below is my implementation
number of rows
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSArray* reqFdate= [TableViewController getrequestPerDate];
NSLog(#"Count OF REQUESTS: %d",reqFdate.count);
if (reqFdate.count == 0 ) {
NSInteger numberOfRows = [self.persons count];
if ([self datePickerIsShown]){
numberOfRows++;
}
NSLog(#"NO OF ROWS in IF %d",numberOfRows);
return numberOfRows;
}
else{
NSLog(#"NO OF ROWS %d",reqFdate.count);
return reqFdate.count;
}
}
cell for row at indexpath
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell;
NSDate *today = [NSDate date];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
// display in 12HR/24HR (i.e. 11:25PM or 23:25) format according to User Settings
[dateFormatter setTimeStyle:NSDateFormatterShortStyle];
NSString *currentTime = [dateFormatter stringFromDate:today];
NSDate *date=[dateFormatter dateFromString:currentTime];
if(indexPath.row==0){
VCPerson *person = self.persons[0];
cell = [self createPersonCell:person];
}
else if ([self datePickerIsShown] && (self.datePickerIndexPath.row == 1)){
// VCPerson *person = self.persons[indexPath.row -1];
cell = [self createPickerCell:date];
}
else{
TacleCell *cell = (TacleCell*)[self.tableView dequeueReusableCellWithIdentifier:otherCellID];
cell.delegate = self;
if(cell == nil)
{
cell = [[TacleCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:otherCellID];
}
else
{
//cancel loading previous image for cell
[[AsyncImageLoader sharedLoader] cancelLoadingImagesForTarget:cell.profileImage];
}
return cell;
}
return cell;
}
didSelect method
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[self.tableView beginUpdates];
int countOfRowss = [TableViewController getrequestPerDate].count;
if(countOfRowss>0)
{
NSArray* indexpathsArry = [self getIndexPaths];
[self.tableView deleteRowsAtIndexPaths:indexpathsArry withRowAnimation:YES];
requestForDate = nil;
}
if ([self datePickerIsShown] && (self.datePickerIndexPath.row - 1 == indexPath.row)){
[self hideExistingPicker];
//[self.tableView reloadData];
//[self viewDidLoad];
//call the service and take the results
NSString* selecteDate = [TableViewController getDate];
//NSString* prsonID =[LoginView getPersonID];
// here i call this method and get the data.
requestForDate= [self filterRequestForDate:selecteDate];
NSLog(#"FILTERED REQUESTS :%#",requestForDate);
//requestForDate = nil;
[self.tableView reloadData];
// NSString* selecteDate = [ScheduleView getDate];
// this is how i fetch the data by using a web service
/* NSString* prsonID =[LoginView getPersonID];
NSDictionary* parms = [NSDictionary dictionaryWithObjectsAndKeys:prsonID,#"caregiverPersonId",selecteDate,#"selectedDate", nil];
jsonpaser* jp = [[jsonpaser alloc]init];
//[self.indicator_process startAnimating];
[jp getWebServiceResponce:#"myUrl" :parms success:^(NSDictionary *responseObject)
{
requestForDate = responseObject;
NSLog(#"RESPONSEFORDATE_IN DIDSELECT :%#",requestForDate);
NSArray* indexpaths = [self getIndexPaths];
NSLog(#"indexPATHS %#",indexpaths);
//[self.indicator_process stopAnimating];
//[_dimmingView removeFromSuperview];
[self.tableView reloadData];
}];
*/
}
else{
//--
NSIndexPath *newPickerIndexPath = [self calculateIndexPathForNewPicker:indexPath];
if ([self datePickerIsShown]){
[self hideExistingPicker];
}
[self showNewPickerAtIndex:newPickerIndexPath];
self.datePickerIndexPath = [NSIndexPath indexPathForRow:newPickerIndexPath.row + 1 inSection:0];
NSLog(#"DATEPICKERINDEX %#",self.datePickerIndexPath);
//self.datePickerIndexPath = nil;
}
// this is the line where a row deleted
[self.tableView deselectRowAtIndexPath:indexPath animated:YES];
[self.tableView endUpdates];
}
please someone tell me where is the issue...
thank you

Regarding to apple documentation apple link you should not call reloadData between beginUpdates and endUpdates.
You should remove the beginUpdates and endUpdates or move the reloadData outside the group.

Related

Invalid update: invalid number of rows in section

I am working on a project using Microsoft Azure services. In that while deleting a row I am getting this error:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (2) must be equal to the number of rows
contained in that section before the update (2), plus or minus the number of rows inserted or deleted from that section (0 inserted, 1 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).
Code for table load and delete row is as :
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 3;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
//medicine list
if (section == 0) {
NSArray *sectionInfo = [self.fetchedResultsController1 fetchedObjects];
return [sectionInfo count];
}
//allergy list
else if (section == 1) {
NSArray *sectionInfo = [self.fetchedResultsController2 fetchedObjects];
return [sectionInfo count];
}
//notes list
else if (section == 2){
NSArray *sectionInfo = [self.fetchedResultsController3 fetchedObjects];
return [sectionInfo count];
}
else
return 0;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"mcell";
MedicationTableViewCell *cell = (MedicationTableViewCell *) [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
// Add utility buttons
NSMutableArray *rightUtilityButtons = [NSMutableArray new];
[rightUtilityButtons sw_addUtilityButtonWithColor: [UIColor colorWithRed:1.0f green:0.231f blue:0.188 alpha:1.0f] title:#"Delete"];
switch (indexPath.section) {
case 0: {
NSManagedObject *item = [self.fetchedResultsController1 objectAtIndexPath:indexPath];
cell.textLabel.text = [item valueForKey:#"name"];
break;
}
case 1: {
NSManagedObject *item = [self.fetchedResultsController2 objectAtIndexPath:[NSIndexPath indexPathForRow:indexPath.row inSection:0]];
cell.textLabel.text = [item valueForKey:#"name"];
break;
}
case 2: {
NSManagedObject *item = [self.fetchedResultsController3 objectAtIndexPath: [NSIndexPath indexPathForRow:indexPath.row inSection:0]];
cell.textLabel.text = [item valueForKey:#"title"];
break;
}
default:
break;
}
cell.rightUtilityButtons = rightUtilityButtons;
cell.delegate = self;
return cell;
}
- (void)swipeableTableViewCell:(SWTableViewCell *)cell didTriggerRightUtilityButtonWithIndex:(NSInteger)index {
// Delete button is pressed
NSIndexPath *cellIndexPath = [self.medicationsTableView indexPathForCell:cell];
//fetchingg data from local store first
NSManagedObject *item = [self.fetchedResultsController1 objectAtIndexPath:[NSIndexPath indexPathForRow:cellIndexPath.row inSection:0]];
NSMutableArray *keys = [[NSMutableArray alloc] initWithObjects:#"id", #"name", #"userId", nil];
NSMutableArray *values = [[NSMutableArray alloc] initWithObjects: [item valueForKey:#"id"], [item valueForKey:#"name"], [item valueForKey:#"userId"], nil];
//creating dictionary of data
NSDictionary *dict = [[NSDictionary alloc] initWithObjects:values forKeys:keys];
//calling delete fucntion
[self.authService deleteItem:self.syncTable withDict:dict completion:^{
//removing row from table view
[self.medicationsTableView reloadData];
[self.medicationsTableView deleteRowsAtIndexPaths:#[cellIndexPath] withRowAnimation:UITableViewRowAnimationRight];
}];
break;
}
Please tell where I am going wrong. Thanks in advcance!!!!
In - (void)swipeableTableViewCell: didTriggerRightUtilityButtonWithIndex:
You need to remove either
[self.medicationsTableView reloadData];
or
[self.medicationsTableView deleteRowsAtIndexPaths:#[cellIndexPath] withRowAnimation:UITableViewRowAnimationRight];
Because on first line when reloadData get called it's reload the tableview with new datasource and again calling deleteRowsAtIndexPaths tried to delete a rows which already removed by calling reloadData earlier.
The important thing is that you need to use either
[self.medicationsTableView reloadData];
or
[self.medicationsTableView deleteRowsAtIndexPaths:#[cellIndexPath] withRowAnimation:UITableViewRowAnimationRight];
The reason being, when the tableview's reload data is called the row which is removed from data source is deleted and after that when the delete row api is called for the same index path(where the row is already deleted causes the issue)
Also just before deleting the row from the table delete the object from the data source (self.fetchedResultsController1) and call
[self.medicationsTableView deleteRowsAtIndexPaths:#[cellIndexPath] withRowAnimation:UITableViewRowAnimationRight];
and after the above row's deletion animation is completed than you can call(but it is not required)
[self.medicationsTableView reloadData];

UITableView UI Will Not Refresh to Data from New NSFetchedResultsController

I have a UITableView which is being filled from Core Data by a NSFetchedResultsController. Everything works great. However, I have just implemented a UISegmentedControl at the top of the UITableView, which I would like to sort the results. There are three segments: #"All", #"Boys", #"Girls". In viewDidLoad, I instantiate a NSDictionary with three NSFetchedResultsController. Each has the exact same fetch request with a different predicate.
allFetchRequest.predicate = [NSPredicate predicateWithFormat:#"school.schoolID == %#", [NSNumber numberWithInt:[_schoolID intValue]]];
boysFetchRequest.predicate = [NSPredicate predicateWithFormat:#"school.schoolID == %# AND gender == %#", [NSNumber numberWithInt:[_schoolID intValue]], #"M"];
girlsFetchRequest.predicate = [NSPredicate predicateWithFormat:#"school.schoolID == %# AND gender == %#", [NSNumber numberWithInt:[_schoolID intValue]], #"F"];
When the UISegmentControl value is changed, I call a method which changes the view controller's "currentFetchedResultsController" instance variable to the corresponding NSFetchedResultsController for that segment, calls perform fetch, then calls reloadData on the tableView in the main thread.
- (void)showBoys
{
self.currentFetchedResultsController = self.fetchResultsControllerDictionary[#"boys"];
[self.currentFetchedResultsController performFetch:nil];
[self.tableView performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:NO];
}
It all seems to work great, except that the UITableView never seems to update its UI. It seems to show the correct number of sections for all, boys, and girls, but the object at each index doesn't change. For example, let's say we have 11 people in Core Data. Five guys, six girls. The view loads with the "All" segment pre-selected, so all 11 people load into the UITableView. However, when I switch the segment to "Boys", the number of rows will drop to five, but the objects in those rows never change. The UITableView will continue to show the first five objects that were already in the table, even if some are girls (gender == "F" in Core Data).
I know that the fetch is working properly because I have set up a small test:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
User *user = [self.currentFetchedResultsController objectAtIndexPath:indexPath];
NSLog(#"Username = %# & Gender = %#", user.username, user.gender);
}
Now, when I select a row, it logs the correct username and gender that SHOULD be at that indexPath. However, the logged username is different than the one that appears in the UITableView at that row.
Table View Data Source Methods:
- (UserTableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Home Ranked Cell";
UserTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
User *user = [self.currentFetchedResultsController objectAtIndexPath:indexPath];
NSString *name = user.name;
if(!name || [name isEqualToString:#""])
name = user.username;
cell.name.text = name;
UserTableViewCell *previousCell = nil;
if(indexPath.row != 0)
previousCell = (UserTableViewCell *)[self tableView:tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:indexPath.row - 1 inSection:indexPath.section]];
NSInteger previousVotes = 0;
if(previousCell)
previousVotes = [previousCell.votes.text integerValue];
if(!previousCell) {
cell.rank.text = [NSString stringWithFormat:#"%i", 1];
} else if(previousVotes == [user.votes integerValue]) {
cell.rank.text = previousCell.rank.text;
} else {
cell.rank.text = [NSString stringWithFormat:#"%i", [previousCell.rank.text integerValue] + 1];
}
if(user.profilePicture && user.profilePicture.thumbnailData && ![user.profilePicture.thumbnailData isEqualToString:#""]) {
NSData *imageData = [[NSData alloc] initWithBase64EncodedString:user.profilePicture.thumbnailData options:0];
cell.imageView.image = [UIImage imageWithData:imageData];
}
if(!cell.imageView.image)
cell.imageView.image = [UIImage imageNamed:#"xIcon.png"];
cell.votes.text = [NSString stringWithFormat:#"%i", [user.votes integerValue]];
cell.upButton.tag = indexPath.row;
cell.downButton.tag = indexPath.row;
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
User *user = [self.currentFetchedResultsController objectAtIndexPath:indexPath];
NSLog(#"Username = %# & Gender = %#", user.username, user.gender);
}
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView
{
return [self.currentFetchedResultsController sectionIndexTitles];
}
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index
{
return [self.currentFetchedResultsController sectionForSectionIndexTitle:title atIndex:index];
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
return self.segmentControl;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [[self.currentFetchedResultsController sections] count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
id sectionInfo = [[self.currentFetchedResultsController sections] objectAtIndex:section];
return [sectionInfo numberOfObjects];
}
I have tried nearly everything. I have gone through every related thread and nothing has worked. What am I doing wrong?
** Newest Findings:
When a cell is selected, the cell does not become highlighted (or show any UI change for that matter), unless it is the correct cell for that specific index path. For example, let's say Sally is in row 0 for all Users, and Tom is in row 1. If I switch the UISegmentedControl to "Male" and tap the first cell (row 0, which currently shows Sally), there is absolutely no UI indication that the cell has been tapped, although tableView: didSelectRowAtIndexPath still gets called, logging the cell information that belongs there (Tom's User info, since he belongs in row 0 of the "Male" Users).
It seems to me that it would be easier to put the segmentation logic into the fetched results controller method. When switching the segmented control, just set your FRC to nil and account for the proper filter in the FRC creation code. You do not need 3 FRCs. Thus:
-(void)segmentedControlDidChange:(UISegmentedControl*)control {
self.fetchedResultsController = nil;
[self.tableView reloadData];
}
and when creating the FRC:
NSPredicate *basePredicate = [NSPredicate predicateWithFormat:
#"school.schoolID = %#", _schoolID];
NSPredicate *secondPredicate = [NSPredicate predicateWithValue:YES];
NSInteger i = self.segmentedControl.selectedSegmentIndex;
if (i > 0) {
secondPredicate = [NSPredicate predicateWithFormat:
#"gender = %#", i == 1 ? #"M" : #"F"];
}
fetchRequest.predicate = [NSCompoundPredicate
andPredicateWithSubPredicates:#[basePredicate, secondPredicate]];
It turns out I made a silly mistake when initializing my UITableViewCell. Instead of calling
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
I was calling
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier **forIndexPath:indexPath**];
The latter returns the current UITableViewCell dequeued from the indexPath mentioned. For some reason, if the cell is dequeued from an existing cell, the properties on the cell are readonly, causing all of my changes to be simply ignored. I hope this helps someone in the future, as I did not see anything about this on Stack Overflow, I just happened to stumble upon my mistake after hours of analyzing each line of code.

Inline date picker crashed on second attempt

I have been struggling for this issue for two week now. but by the help of StackOverflow people I have came up with 95% successful implementation..
Here is my problem.. and now I could load my results with the picker cell [First most cell] when the date is selected.
Now I have another issue...when i run my application at first time, it is works as I expected..but when i tap the first most cell to pick a different date , then the application crashed..
Here is the log output...
below is my codes for the implementation
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSDictionary* reqFdate= [ScheduleView getRequestForDate];
if (reqFdate.count == 0) {
NSInteger numberOfRows = [self.persons count];
if ([self datePickerIsShown]){
numberOfRows++;
}
return numberOfRows;
}
else{
return reqFdate.count + 1;
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell;
NSDate *today = [NSDate date];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
// display in 12HR/24HR (i.e. 11:25PM or 23:25) format according to User Settings
[dateFormatter setTimeStyle:NSDateFormatterShortStyle];
NSString *currentTime = [dateFormatter stringFromDate:today];
NSDate *date=[dateFormatter dateFromString:currentTime];
if(indexPath.row==0){
VCPerson *person = self.persons[0];
cell = [self createPersonCell:person];
}
else if ([self datePickerIsShown] && (self.datePickerIndexPath.row == 1)){
// VCPerson *person = self.persons[indexPath.row -1];
cell = [self createPickerCell:date];
}
else{
cellForDatePickCell *cell = (cellForDatePickCell*)[self.tableView dequeueReusableCellWithIdentifier:kOtherCellIdentifier];
cell.delegate_Dtepick = self;
return cell;
}
if(indexPath.section!=0 && tapfirstCell) {
UITableViewCell *cell = (UITableViewCell*)[self.tableView dequeueReusableCellWithIdentifier:kOtherCellIdentifier forIndexPath:indexPath];
//cell.delegate_Dtepick = self;
NSDictionary *dictionary = [_dataArray objectAtIndex:indexPath.section];
NSArray *array = [dictionary objectForKey:#"data"];
NSString *cellValue = [array objectAtIndex:indexPath.row];
cell.textLabel.text =cellValue;
}
return cell;
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[self.tableView beginUpdates];
if ([self datePickerIsShown] && (self.datePickerIndexPath.row - 1 == indexPath.row)){
[self hideExistingPicker];
// [self.tableView reloadData];
//[self viewDidLoad];
//call the service and take the results
NSString* selecteDate = [ScheduleView getDate];
NSString* prsonID =[LoginView getPersonID];
NSDictionary* parms = [NSDictionary dictionaryWithObjectsAndKeys:prsonID,#"caregiverPersonId",selecteDate,#"selectedDate", nil];
jsonpaser* jp = [[jsonpaser alloc]init];
[jp getWebServiceResponce:#"http://qa.vardle.com/Mobile/WebServices/AppointmentService.asmx/GetAppointments" :parms success:^(NSDictionary *responseObject)
{
requestsF_date = responseObject;
NSLog(#"RESPONSEFORDATE_IN DIDSELECT :%#",requestsF_date);
NSArray* indexpaths = [self getIndexPaths];
NSLog(#"indexPATHS %#",indexpaths);
[self.tableView reloadData];
}];
// cellForDatePickCell *cell = (cellForDatePickCell*)[self.tableView dequeueReusableCellWithIdentifier:kOtherCellIdentifier forIndexPath:indexPath];
// cell.delegate_Dtepick = self;
//tapfirstCell = true;
/*
cellForDatePickCell *cell=(cellForDatePickCell*)[tableView cellForRowAtIndexPath:indexPath];
if(![cell.textLabel.text isEqualToString:#"5/23/14"])
{
return;
}
*/
if (tapfirstCell==false) {
tapfirstCell = true;
}
else{
tapfirstCell = false;
}
}else {
NSIndexPath *newPickerIndexPath = [self calculateIndexPathForNewPicker:indexPath];
if ([self datePickerIsShown]){
[self hideExistingPicker];
}
[self showNewPickerAtIndex:newPickerIndexPath];
self.datePickerIndexPath = [NSIndexPath indexPathForRow:newPickerIndexPath.row + 1 inSection:0];
}
[self.tableView deselectRowAtIndexPath:indexPath animated:YES];
[self.tableView endUpdates];
}
please someone tell me where is the issue..why app is crashed when i try to pick a date second time..
please help
i have fix my issue... i didnt deleted the rows before i start to show the date picker again..
here is the full code
here what i did was : i use Inline Datepicker with the table view [date picker will show by selecting the first cell of the table view].according to the date selected from the picker, i am displaying my custom cells below the first cell.[because the first cell always there to pick a date from a date picker].
if someone want to do the same thing which i did i think my code will help you
thank you

tableView:numberOfRowsInSection: called twice in a row

I'm getting an error:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0.
because I'm adding an object to one instance of an array, but then reloading the table view from another instance of what is supposed to be the same array. How can I create a single instance of the array and just pass it around from class to class? I can do it easily in java, but in Objective-C you can't make static variables so I'm not sure how to do this.
EDIT: more code.
Here is a method from another class that is called in order to save a file. I'm using Core Data, so first it adds the file to the context (model), then to the array, then it saves the context. This method is in a class called 'Player'
-(BOOL)saveRecording {
Bank *B = [MusikerViewController daBank];
AudioTableViewController *ATVC2 = [MusikerViewController ATVControl];
NSLog(#"Place A");
AudioFile *myAudioFileMusicX314 = [[B addAudioFileEntityToModelWithDate:myDate andURLString:strPath] retain];
NSLog(#"Place B");
myAudioFileMusicX314.type = true;
[ATVC2 addAudioEntityToArray:myAudioFileMusicX314];
NSLog(#"Place C ***********************************************");
if(![B saveContext]) { //save context after adding file to keep consistancy
NSLog(#"addAudioFileEntityToModel is returning a nil managedObjectContext");
return NO;
}
NSLog(#"Place D");
[myDate release];
[strPath release];
[myAudioFileMusicX314 release];
[ATVC2 release];
NSLog(#"Place E");
return YES;
}
The following method is in the class that contains the table view--its caled AudioTableViewController
-(void)addAudioEntityToArray:(AudioFile *)event {
NSIndexPath *indexPath;
if(event.type) {
[[MusikerViewController recordingsArray] addObject:event];//self?
indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
}
else {
[[MusikerViewController downloadsArray] addObject:event];
indexPath = [NSIndexPath indexPathForRow:0 inSection:1];
}
[[self tableView] setEditing:YES animated:NO];
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationNone];
}
The following method adds the object to my model
- (AudioFile *)addAudioFileEntityToModelWithDate:(NSDate *)theD andURLString:(NSString *)str {
NSLog(#"addAudio...WithDate -- called");
sound = (AudioFile *)[NSEntityDescription insertNewObjectForEntityForName:#"AudioFile" inManagedObjectContext:managedObjectContext];
sound.creationDate = theD;
sound.soundPath = str; //set the sound path to the sound file's url
[self alertForTitle];
NSLog(#"No problems yet at place D - addaudio...String, sound title is %#", sound.title);
NSLog(#"Context at addAudioFileEntityToModel is: %#", managedObjectContext);
return sound;
}
here is the important parts of MusikViewController.h -- it keeps track of recordingsArray and downloadsArray
#interface MusikerViewController : UIViewController {
}
NSMutableArray *recordingsArray;
NSMutableArray *downloadsArray;
+ (NSMutableArray *)recordingsArray;
+ (NSMutableArray *)downloadsArray;
and MusikViewController.m
+ (NSMutableArray *)recordingsArray {
NSLog(#"recordingsArray called");
if(!recordingsArray) {
recordingsArray = [[NSMutableArray alloc] init];
NSMutableArray *bigTempArray = [[[[Bank alloc] init] autorelease] getFetchArray]; //change this
for(AudioFile *af in bigTempArray)
if(af.type) {
[recordingsArray addObject:af];
}
NSLog(#"recordingsArray exists");
}
return recordingsArray;
}
+ (NSMutableArray *)downloadsArray {
NSLog(#"recordingsArray called");
if(!downloadsArray) {
downloadsArray = [[NSMutableArray alloc] init];
// if(!bigTempArray)
NSMutableArray *bigTempArray = [[[[Bank alloc] init] autorelease] getFetchArray];
for(AudioFile *af in bigTempArray)
if(!af.type) {
[downloadsArray addObject:af];
}
}
return downloadsArray;
}
and some AudioTableViewController methods
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
NSLog(#"Place F");
if(section == 0) {
return [[MusikerViewController recordingsArray] count];
}
else if (section == 1) {
return [[MusikerViewController downloadsArray] count];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (!cell) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
}
AudioFile *event;
if(indexPath.section == 0) {
event = (AudioFile *)[[MusikerViewController recordingsArray] objectAtIndex:indexPath.row];
} else if (indexPath.section == 1) {
NSLog(#"downAry indexPath caled at cellForRow...Path");
event = (AudioFile *)[[MusikerViewController downloadsArray] objectAtIndex:indexPath.row];
}
cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
if(event.title) {
cell.detailTextLabel.text = [Player dateString:event.creationDate];
cell.textLabel.text = event.title;
} else {
cell.textLabel.text = [Player dateString:event.creationDate];
cell.detailTextLabel.text = nil;
}
return cell;
}
- (void)viewDidLoad //viewDidLoad for AudioTableViewController
{
[[self tableView] reloadData];
NSLog(#"viewDidLoad called for AudioTableViewController");
[super viewDidLoad];
self.title = #"Audio Files";//put this in application delegate
// Set up the buttons.
self.navigationItem.leftBarButtonItem = self.editButtonItem;
}
If your data model changes you need to either reload the tableView, or insert/remove the rows otherwise the tableview will freak out on you. It doesn't matter how many times tableView:numberOfRowsInSection is called, your data model can't return a different number without first reloading or updating the tableview.
Based on your logging my guess is that your downloads array is changing quantities. It makes it past the NSLog then dies in _endCellAnimationsWithContext.

delete last row in TableView and show a "no results" label in new cell

I have a problem with my UITableView where deleting the last row in the section terminates the app with an NSInternalInconsistencyException:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (1) must be equal to the number of rows contained in that section before the update (1), plus or minus the number of rows inserted or deleted from that section (0 inserted, 1 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).'
My UITableView is populated with MPMediaItems from an MPMediaItemCollection (self.detailCollection). When the last one gets deleted I want to show a "No results found" label in a blank cell.
Here is my cellForRowAtIndexPath:
- (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...
if ([[self.detailCollection items] count] == 0) {
[tableView numberOfRowsInSection:1];
cell.textLabel.text = #"No results found";
//return cell;
} else {
MPMediaItem *song = (MPMediaItem *)[[self.detailCollection items] objectAtIndex:[indexPath row]];
if (song) {
cell.textLabel.text = [song valueForProperty:MPMediaItemPropertyTitle];
MPMediaItemArtwork *art = [song valueForProperty:MPMediaItemPropertyArtwork];
cell.imageView.image = [art imageWithSize:CGSizeMake(64, 64)];
}
}
return cell;
}
Here is my code for deleting the rows:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
PlaylistData *pData = [PlaylistData getInstance];
NSMutableArray *tempArray = [[self.eventDictionary valueForKey:[NSString stringWithFormat:#"%#", pData.selectedEvent]] mutableCopy];
NSMutableArray *newArray = [[NSMutableArray alloc] init];
if (editingStyle == UITableViewCellEditingStyleDelete) {
[tableView beginUpdates];
// Delete the row from the data source
[tempArray removeObjectAtIndex:indexPath.row];
[self.eventDictionary setValue:tempArray forKey:[NSString stringWithFormat:#"%#", pData.selectedEvent]];
[[NSUserDefaults standardUserDefaults] setValue:self.eventDictionary forKey:#"Playlist Items"];
[[NSUserDefaults standardUserDefaults] synchronize];
if ([tempArray count] == 0) {
[tableView numberOfRowsInSection:1];
}
for (int i=0; i<[tempArray count]; i++) {
NSString *pID = [NSString stringWithFormat:#"%#", [tempArray objectAtIndex:i]];
unsigned long long ullvalue = strtoull([pID UTF8String], NULL, 0);
NSNumber *UniqueID = [NSNumber numberWithUnsignedLongLong:ullvalue];
MPMediaQuery *cellQuery = [[MPMediaQuery alloc] init];
[cellQuery addFilterPredicate:[MPMediaPropertyPredicate predicateWithValue:UniqueID forProperty:MPMediaItemPropertyPersistentID]];
for (MPMediaItem *item in [cellQuery items]) {
[newArray addObject:item];
}
[cellQuery release];
}
if (![newArray count] == 0) {
self.detailCollection = [[MPMediaItemCollection alloc] initWithItems:newArray];
[tableView numberOfRowsInSection:[self.detailCollection count]];
} else {
[tableView numberOfRowsInSection:1];
[tableView reloadData];
}
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView endUpdates];
}
else if (editingStyle == UITableViewCellEditingStyleInsert) {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
And here is my numberOfRowsInSection:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
if ([[self.detailCollection items] count] == 0 || [self.detailCollection items] == nil || [self.detailCollection items] == NULL) {
return 1;
}
return [[self.detailCollection items] count];
}
My question is: Why isn't it creating the "No results found" cell when self.detailCollection is == 0?
I think you want something to the effect of:
[tableView beginUpdates];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
if ([newArray count] == 0) {
[tableView insertRowsAtIndexPaths:#[[NSIndexPath indexPathForRow:0 inSection:0]] withRowAnimation:UITableViewRowAnimationAutomatic];
}
[tableView endUpdates];
However, a simpler solution would be to just add a label to your table view. Unless there is some specific reason that you need an actual UITableViewCell to display "No results found".
UILabel *label = [[UILabel alloc] init];
CGRect frame = CGRectMake(0.0, 0.0, 320.0, 44.0);
label.frame = frame;
label.text = #"No results found";
[self.tableView addSubview:label];
One solution that I would recommend is to use a table footer view rather than a new cell. Basically, add a footer to your table that is only visible when the cell count is 0.
You can override the method
- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section
to get a footer.
When deleting and adding objects, check the new count and then adjust the visibility of the footer from there.
You are calling numberOfRowsInSection in a couple of places. You should never be calling it, it's a call back hook that you implement and the system calls.
The cleanest solution to do this would be to set self.tableView.tableFooterView when rows = 0
#interface UITableView : UIScrollView <NSCoding> {
...
#property(nonatomic,retain) UIView *tableFooterView;
// accessory view below content. default is nil. not to be confused with section footer

Resources