So I am trying to select objects from my array, to be able to delete them when I do an IBAction. I tried:
checking if the item is Selected:
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
if (self.editEnabled) {
RDNote *selectedNote = [self.notes objectAtIndex:indexPath.row];
if (selectedNote.isSelected) {
selectedNote.selected = NO;
for (NSIndexPath *indexPathFromArray in self.indexPathsOfSelectedCells) {
if (indexPathFromArray.row == indexPath.row) {
[self.mutableCopy removeObject:indexPathFromArray];
}
}
} else {
selectedNote.selected = YES;
[self.indexPathsOfSelectedCells addObject:indexPath];
}
[self.collectionView reloadData];
IBAction:
- (IBAction)didTapTrashBarButton:(id)sender {
NSMutableArray *mutableNotes = [NSMutableArray arrayWithArray:self.notes];
for (NSIndexPath *indexPath in self.indexPathsOfSelectedCells) {
[mutableNotes removeObjectAtIndex:indexPath.row];
}
self.notes = [NSArray arrayWithArray:mutableNotes];
[self.collectionView performBatchUpdates:^{
[self.collectionView deleteItemsAtIndexPaths:[NSArray arrayWithArray:self.indexPathsOfSelectedCells]];
} completion:^(BOOL finished) {
self.indexPathsOfSelectedCells = nil;
[self activateEditMode:NO];
[self saveDataToFile:self.notes];
}];
}
But I am having issues with indexes e.g.:(some times shows me the Error that Object index 2 is not between [0..1]), and there are bugs while selecting multiple Object and Deleting them.
Please help me with an advice for some other method that I can use, a Code will be perfect! Thanks!
This problem arrises because:
Let say you have five objects in array 1,2,3,4,5
you are running a loop for removing object based on indexpath which are selected rows. Now your indexpath contains 1st row and 3rd row.
while executing it for the first time you will remove object 1. Now 2,3,4,5 will left in the array. Now second time your indexpath.row is 3rd. It will remove third object which is 4 but in actual array it was 3.
Your code is crashing sometimes because if you have selected first row and last row. In this case i have selected 1 and 5. Now my indexpaths array will say I have to delect objectsAtIndexes 1 and 5.
While executing the loop I'll remove object at Index 1. Now i will be left with 2,3,4,5. On second iteration it will say remove objectAtIndex 5 where as Index 5 doesn't exist because now we have 4 elements in array.
In such cases best way of doing this is try removing elements in array from the end like remove 5th element first and then go for other one. Run your loop in reverse order.
NSInteger i = [self.indexPathsOfSelectedCells count]-1;
while (i > 0){
NSIndexPath *indexPath = [self.indexPathsOfSelectedCells objectAtIndex:i];
[mutableNotes removeObjectAtIndex:indexPath.row];
i--;
}
Related
My data structor looks like this:
Inside an NSArray I have a few objects from different classes. How the array looks like if something like this:
Let's say myArray has 3 objectAs. Each objectA and an NSArry that has objectBs
Now at runtime I do not know the array counts.
My UITableView displays one objectA per section. Each section has 1 row. Unless my objectA has an NSArray of objectBs with a count of greater than 0.
So the UITableView would look like this if all my objectA's has no arrays of objectB's in them.
Section 1: Row 1: `objectA instance
Section 2: Row 1: `objectA instance
Section 3: Row 1: `objectA instance
Lets say the 3rd objectA has an array of objectB's with a count of 1. My UITableView would look like this:
Section 1: Row 1: `objectA instance
Section 2: Row 1: `objectA instance
Section 3: Row 1: `objectA instance
Section 3: Row 2: `objectB instance
Now I am using two different UITableViewCells for each object so objectA would use CustomCell1 and objectB would use CustomCell2
Here is where I am getting stuck - I need to return the correct cell to the correct section / row.
So I made sure my UITableView knows what to expect.
I wrote the methods like so:
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
ObjectA *objectA = self.myArray[section];
return [objectA.objectBArray count] +1;
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [self.myArra count];
}
So numberOfRowsInSection takes a look at the objectBs array and returns its count + 1 so that even if its 0 there is always one row. (I need at least one row for objectA in the current section.
numberOfSectionsInTableView is straight forward - one section for each objectA in self.myArray
This seems to do exactly as I need.
I then got stuck on cellForRowAtIndexPath method.
This is how far I got:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
CustomCell1 *cell1 = [tableView dequeueReusableCellWithIdentifier:#"cell1"];
CustomCell2 *cell2 = [tableView dequeueReusableCellWithIdentifier:#"cell2"];
ObjectA *objectA = self.myArray[indexPath.section]; //Each section has one ObjectA.
//Make sure we have some objectB's to display first
if ([objectA. objectBArray count] > 0){
ObjectB *objectb = (ObjectB*) objectA.objectB[indexPath.row];
//if the current section (section 3) has a indexPath 0 already
// index path 1 would have the first objectB
}
cell1.objectA = objectA;
cell2.objectB = objectB;
//Now return the correct cell for the current section / index path
}
So I need to check if the current section already has valid cell1 object if it does I then need to return cell2 to the second or third row. for objectB As row one must always have a cell1.
I know I am almost there. I just can't figure out how to return the correct cell.
Update
So the cellForRowAtIndexPath method is only being called three times. Here is what I did in numberOfRowsInSection method to make sure it returns the right number of rows:
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
ObjectA *objectA = self.myArray[section];
NSLog (#"Section:%ld", (long)section);
NSLog(#"Returning %ld", (long)[objectA.objectBArray count]+1);
return [objectA.objectBArray count] +1;
}
My console shows this:
2014-07-16 19:36:17.488 App[15473:60b] Section:2
2014-07-16 19:36:17.488 App[15473:60b] Returning 2
2014-07-16 19:36:18.128 App[15473:60b] Section:0
2014-07-16 19:36:18.128 App[15473:60b] Returning 1
2014-07-16 19:36:19.063 App[15473:60b] Section:1
2014-07-16 19:36:19.063 App[15473:60b] Returning 1
Any ideas?
If there will always only be one objectA in each section then you can test if your indexPath.row is anything larger then zero (not the first row).
See if this does the trick:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
CustomCell1 *cell1 = [tableView dequeueReusableCellWithIdentifier:#"cell1"];
CustomCell2 *cell2 = [tableView dequeueReusableCellWithIdentifier:#"cell2"];
ObjectA *objectA = self.myArray[indexPath.section]; //Each section has one ObjectA.
cell1.objectA = objectA;
//Check if not first row and objectB's exist
if (indexPath.row > 0 && [objectA. objectBArray count] > 0){
ObjectB *objectb = (ObjectB*) objectA.objectB[indexPath.row];
cell2.objectB = objectB;
return cell2;
}
return cell1;
}
This is an answer to the second issue I was having. After setting breakpoints in my view controller - I noticed my tableView was already loading cells before my call to [self.tableView reloadData];
Not sure why that was happening so a quick nil-check in my rows for items method solved the issue:
if the main array had no objects - return 0. If it did - return objectBArray count + 1; Now the cellForRowAtIndexPath is called the correct number of times.
(Using X-Code 4.6)
I have a problem with my TableView:
I have like a 100 Names in there.
Click on one and it goes to the detailView with buttons and Labels.
Everything works fine until i use the Searchbar.
Of course after in the search Results the name that was at row 85 is ow at row 1 and it passes the data from row 1.
Is it possible to somehow permanently attach all data (strings) from row 85 together?
I have 4 Strings: Name, CellPhoneNumber, BuisNumber and Mail.
Name goes to a label and the rest are variables to buttons.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"toDetail"]) {
DetailViewController *destViewController = segue.destinationViewController;
NSIndexPath *indexPath = nil;
if ([self.searchDisplayController isActive]) {
indexPath = [self.searchDisplayController.searchResultsTableView indexPathForSelectedRow];
destViewController.nameIdentity = [searchResults objectAtIndex:indexPath.row];
}
else {
indexPath = [self.tableView indexPathForSelectedRow];
destViewController.nameIdentity = [nameData objectAtIndex:indexPath.row];
destViewController.cellNumberIdentity = [cellNumberData objectAtIndex:indexPath.row];
destViewController.buisNumberIdentity = [buisNumberData objectAtIndex:indexPath.row];
destViewController.mailIdentity = [mailData objectAtIndex:indexPath.row];
} }
}
I do get the correct Name to the label, but how to get the rest.
If I understood you right it will be better to create class e.g. Person. it will have 4 parameters name, mail, cellNumber, businessNumber.
Then You will need 2 arrays fullPersonData and currentSearchPersonData.
So that next time when you will doing search you will need:
1)to search in fullPerson array occurrences of search word and copy them to currentSearchData
2)reload table with currentSearchData array (you can use bool flag like isSearch to trig tableView delegates to take data from needed array)
After that there is no need to think on what row was cell before search just toke objectAtIndex from neded array and pass it to detailView. and detailView will fill itself))
I have a problem with my moveRowAtIndexPath:toIndexPath code.
It works fine when I'm moving cells within a section but when I try to move a row from one section (say from indexPath [1,1] to indexPath [2,0]) to another I get a crash with this error message:
* Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid
number of rows in section 1. 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,
0 deleted) and plus or minus the number of rows moved into or out of
that section (0 moved in, 1 moved out).'
Here is my code, again, it works fine if the two indexPaths share the same section number:
- (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath
{
NSMutableArray *cells = [[self cellsAtSectionOfIndexPath:indexPath] mutableCopy];
Cell *cell = [self dataAtIndexPath:indexPath];
Cell *newCell = [self dataAtIndexPath:newIndexPath];
if ([indexPath section] == [newIndexPath section])
{
[cells replaceObjectAtIndex:indexPath.row withObject:newCell];
[cells replaceObjectAtIndex:newIndexPath.row withObject:cell];
[self replaceCellsAtIndexPath:indexPath withCells:cells];
}
else
{
NSMutableArray *newCells = [[self cellsAtSectionOfIndexPath:newIndexPath] mutableCopy];
[cells replaceObjectAtIndex:indexPath.row withObject:newCell];
[newCells replaceObjectAtIndex:newIndexPath.row withObject:cell];
[self replaceCellsAtIndexPath:indexPath withCells:cells];
[self replaceCellsAtIndexPath:newIndexPath withCells:newCells];
}
[super moveRowAtIndexPath:indexPath toIndexPath:newIndexPath];
}
How can I fix this?
The problem is with your data source. The error means that you are removing a row from a section but the numberOfRowsInSection: method returns the same number when it should return the old number minus 1. Same thing with adding the row to the other section.
In my cocoa app i have 2 NSTableViews. The AppDelegate is the datasource for both of them. I want to change the data of the second TableView depending on the selected row of the first TableView. For that i use an NSDictionary. I add an array for every entry in the data array of Table1.
- (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView{
if([table1 selectedRow] != -1 && [[aTableView identifier] isEqualToString:#"table2"]){
return [[dict objectForKey:[arrForTable1 objectAtIndex:[table1 selectedRow]]] count];
}
if([[aTableView identifier] isEqualToString:#"table1"]) return [arrForTable1 count];
return 0;
}
- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex{
if([table1 selectedRow] != -1 && [[aTableView identifier] isEqualToString:#"table2"]){
return [[dict objectForKey:[arrForTable1 objectAtIndex:[table1 selectedRow]]] objectAtIndex:rowIndex];
}
if([[aTableView identifier] isEqualToString:#"table1"]){
return [arrForTable1 objectAtIndex:rowIndex];
}
return nil;
}
This code crashes when in table2 a row is selected and after that a switch in the table1 to a row with an empty array.
How can i fix it? I tried to [table2 deselectAll:self] in the - selectionShouldChangeInTableView: method but it doesn't work. It works to trigger the deselectAll: method with an button and that was ok.
Sounds like you might need to call [table2 reloadData] when your selection in table1 changes.
I'm implementing a method to remove a row from my tableview based on a domain object but I'm getting a error after call deleteRowsAtIndexPath:animated
Blockquote 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).'
So I thought "ok, I'm updating the model too soon". The code:
- (void)deleteItem:(Item *)item {
NSInteger index = [items indexOfObject:item]; // items is the model a NSArray
if (index != NSNotFound) {
NSMutableArray *itemEditable = [items mutableCopy];
[itemEditable removeObjectAtIndex:index];
self.items = itemEditable;
NSIndexPath *indexToDelete = [NSIndexPath indexPathForRow:index inSection:0];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexToDelete] withRowAnimation:UITableViewRowAnimationMiddle];
}
}
And then I change the code to update the model after I call deleteRowsAtIndexPath:animated
- (void)deleteItem:(Item *)item {
NSInteger index = [items indexOfObject:item]; // items is the model a NSArray
if (index != NSNotFound) {
NSIndexPath *indexToDelete = [NSIndexPath indexPathForRow:index inSection:0];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexToDelete] withRowAnimation:UITableViewRowAnimationMiddle];
NSMutableArray *itemEditable = [items mutableCopy];
[itemEditable removeObjectAtIndex:index];
self.items = itemEditable;
}
}
But it didn't solve, the only thing that changes is the number of rows in the error message from 2 to 3.
Blockquote 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 (3), plus or minus the number of rows inserted or deleted from that section (0 inserted, 1 deleted).'
Where should I update the model?
Added numberOfRowsInSection method
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [items count];
}
I've found the problem. The setter of items property is implemented to call reloadData and this was causing this confusion.
-(void) setItens:(NSMutableArray *)newItens{
if (itens != nil) {
[itens release];
itens = nil;
}
itens = [newItens copy];
// [tableView reloadData]; // WRONG don't do this
}
This is the second time I found what I was doing wrong just after publishing the question here. I hope this help someone one day. Thanks aBitObvious, I'm aBitShy now :)
I think after deleting the row you need to reload it just below the deleted line:
I mean,
- (void)deleteItem:(Item *)item
{
.......................your code..............
[tableView reloadData];
}
Try this, it should work.