UITableView insert row below tapped cell - objective-c

I'm extremely confused. I have looked at several posts to try to figuere out how to do this. What i would like to do is detect when a UITableViewCell is tapped and then insert a row right below that row with a menu.
I implemented detecting the tapped item but I can't figure out how to insert the new row right below the row that was just tapped.
Here is the method that handles the tapped cell.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
indexPath = nil;
[tableView deselectRowAtIndexPath:indexPath animated:YES];
Locations *location = nil;
if (self.searchDisplayController.active) {
indexPath = [self.searchDisplayController.searchResultsTableView indexPathForSelectedRow];
location= [self.searchResults objectAtIndex:indexPath.row];
NSLog(#"location : %#" ,location.locationName);
[self.locations addObject:#"New Entry"];
[tableView insertRowsAtIndexPaths:#[indexPath] withRowAnimation:YES];
//[self.tableViewForScreen insertRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
} else {
indexPath = [self.tableView indexPathForSelectedRow];
location = [self.locations objectAtIndex:indexPath.row];
NSLog(#"location : %#" ,location.locationName);
[tableView insertRowsAtIndexPaths:#[indexPath] withRowAnimation:YES];
}
[tableView deselectRowAtIndexPath:indexPath animated:YES];
// set the tapped item pointer equals to the locations mutable array variable wich essentially is the
// data source of the table view.
//Locations *tappedItem =[self.locations objectAtIndex:indexPath.row];
//access the location name and print to log.
}
Obviously it fails at the part of insert
[tableView insertRowsAtIndexPaths:#[indexPath] withRowAnimation:YES];
Can someone give me a hand?
The error is the following.
reason: 'Invalid update: invalid number of rows in section 0. The
number of rows contained in an existing section after the update (154)
must be equal to the number of rows contained in that section before
the update (154), plus or minus the number of rows inserted or deleted
from that section (1 inserted, 0 deleted) and plus or minus the number
of rows moved into or out of that section (0 moved in, 0 moved out).'
Here's a post that I looked at.
UITableView action row
http://adcdownload.apple.com//videos/wwdc_2011__sd/session_125__uitableview_changes_tips_tricks.m4v

The problems is that you call insertRowsAtIndexPath: but you don't first update the data source to include the data for the new row.
You seem to do this correctly when selecting a row in the search table but not the main table.

First of all do not use
indexPath = [self.searchDisplayController.searchResultsTableView indexPathForSelectedRow];
you can take indexPath from method parameters
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
It seems that you have problems because you are calling insertRowsAtIndexPaths but you are not updating data source.

Related

Update values inside cell after NSFetchedResultsChangeUpdate

I have a tableview and I link it to core data using NSFetchedResultsController. I have a method updateCellAtIndexPath, that I am using to update cell text label. I call this method from tableView:cellForRowAtIndexPath:indexPath. That works fine.
-(UITableViewCell *)updateCellAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"TripCell";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
Trip *trip = [_controller objectAtIndexPath:indexPath];
cell.textLabel.text= [NSString stringWithFormat:#"%#" ,trip.name ];
return cell;
}
When I update core data object I receive NSFetchedResultsChangeUpdate and I call updateCellAtIndexPath. The problem is that the cell text label is not updated correctly to actual value. I think the cell is not reloaded, because everything works if I change updateCellAtIndexPath to reloadRowsAtIndexPaths:withRowAnimation:
-(void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath
{
switch (type){
case NSFetchedResultsChangeUpdate:
// works [self.tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationNone];
// not works [self updateCellAtIndexPath:indexPath];
break;
default:
break;
}
}
But when I read the documentation for reloadRowsAtIndexPaths:withRowAnimation:, it says that I should not use this method if I want just to update values:
Call this method if you want to alert the user that the value of a
cell is changing. If, however, notifying the user is not
important—that is, you just want to change the value that a cell is
displaying—you can get the cell for a particular row and set its new
value.
Why then I can not update cell text label using just updateCellAtIndexPath?
Your updateCellAtIndexPath: method creates a new cell instead of updating the existing one.
That is OK if called from cellForRowAtIndexPath, but not when called from the FRC
delegate methods.
It should work if you implement and use the "standard"
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
which you can find e.g. when creating a new app using the (Master-Detail + Core Data)
template in Xcode.

UITableView accessoryType disappears in last half of UITableView

I have an iPad app (XCode 4.6, ARC, Storyboards, iOS 6.2.3). I have a UIPopover with a UITableView that has 21 rows in it. I can set the accessoryType in all of the rows randomly, but only in the first 12 rows does the accessoryType setting (checkmark) persist so it can be examined in another method and processed. I don't see any difference between the first 12 rows and the last 9 rows. The UITableView is scrollable, so to get to the rows after the 11th row, you have to scroll to the bottom
Here is the code to set the accessoryType:
#pragma mark didSelectRowAtIndexPath
- (void) tableView:(UITableView *) tableView didSelectRowAtIndexPath: (NSIndexPath *)indexPath {
// get the cell that was selected
UITableViewCell *theCell = [tableView cellForRowAtIndexPath:indexPath];
if(theCell.accessoryType != UITableViewCellAccessoryCheckmark)
theCell.accessoryType = UITableViewCellAccessoryCheckmark;
else
theCell.accessoryType = UITableViewCellAccessoryNone;
}
Here is the code where I check the accessoryType and process it:
-(void) moveServices { // (moves checked tableViewRows to services tableview)
NSMutableString *result = [NSMutableString string];
for (int i = 0; i < [servicesArray count]; i++) {
NSIndexPath *path = [NSIndexPath indexPathForRow:i inSection:0];
[tvServices scrollToRowAtIndexPath:path atScrollPosition:UITableViewScrollPositionMiddle animated:NO];
UITableViewCell *cell = [tvServices cellForRowAtIndexPath:path];
if (cell.accessoryType == UITableViewCellAccessoryCheckmark) {
[result appendFormat:#"%#, ",cell.textLabel.text];
NSLog(#"\n\ni: %d\ncell.accessoryType: %d\ncell.textLabel: %#",i,cell.accessoryType, cell.textLabel);
}
}
if (result.length > 2) { // move to text box in main menu
storeServices =[result substringToIndex:[result length] - 2];
}
}
It looks like you're mixing the notion of "data source" and the contents of the cells in the table. Don't do that -- keep your data source (whether or not a particular row in the table should display a checkmark based on your program logic) separate from the settings of particular cells (whether or not a particular cell displays a checkmark). Then in cellForRowAtIndexPath, you build the cell to match your data source's current settings. The reason is that UITableView reuses cell instances based on which rows are visible on-screen (and it's just good MVC design).
In your case you should keep an NSMutableArray property in your class that records the settings for the entire table, and use the value from that array in cellForRowAtIndexPath to set up that particular cell. Then the other "business logic" methods in your controller use the array property to query your model state instead of the cell settings (which are part of the view and should be independent from the data model).

crash application while delete section from UITableView

In tableview I have many sections, on the top row of section I have a delete button, I want to do delete section after clicking button but its crashing please help me how can I do this?? here is the code...
//****** adding delete section button
CellButton *_sectionButton = (CellButton*)[cell viewWithTag:10];
_sectionButton.hidden = YES;
_sectionButton.indexPath = indexPath;
[_sectionButton addTarget:self action:#selector(sectionDeleteButton:) forControlEvents:UIControlEventTouchUpInside];
-(void)sectionDeleteButton: (id)sender
{
CellButton *_secbtn = (CellButton*)sender;
NSInteger sectionNumber = _secbtn.indexPath.section;
[self.tableView beginUpdates];
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionNumber] withRowAnimation:UITableViewRowAnimationAutomatic];
[self.tableView endUpdates];
[self.tableView reloadData];
}
EDIT: The error messsage is:
Assertion failure in -[UITableView _endCellAnimationsWithContext:], /SourceCache/UIKit_Sim/UIKit-2380.17/UITableView.m:1054 2013-07-04 04:25:15.921 estimation[9025:c07] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of sections. The number of sections contained in the table view after the update (2) must be equal to the number of sections contained in the table view before the update (2), plus or minus the number of sections inserted or deleted (0 inserted, 1 deleted)
I guess the numberOfSectionsInTableView: returns the previous number of sections. You must make sure that what you tell the table view to do also happens in the data source. That is, if you delete a section, then the numberOfSectionsInTableView must also return a number one less than before. The same stands for cells and everything else. This is probably described in the error you get.
You have to manage your number of sections in numberOfSectionsInTableview: delegate method
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
//#warning Potentially incomplete method implementation.
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
//#warning Incomplete method implementation.
// Return the number of rows in the section.
return [MLControl shared].arrBuyList.count;//1;//[MLControl shared].arrBuyList.count;
}
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source
// [tableView beginUpdates];
NSLog(#"indexPath.row=%ld,indexPath.se=%ld",(long)indexPath.row,(long)indexPath.section);
[[MLControl shared].arrBuyList removeObjectAtIndex:indexPath.row];
[tableView deleteRowsAtIndexPaths:#[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
}
}
I met this problem, but the reason is different from yours.
When I add a section for the tableView, I reload data with this method, this also crash logging like yours:
[tableView reloadRowsAtIndexPaths:[tableView indexPathsForVisibleRows] withRowAnimation:UITableViewRowAnimationNone];
Why do I use this method to reload my table? If I only refresh cells, this works fine, but I forgot I want to change the sections. For more information: What is More reliable way to reload UITableView Data?

Duplication in Uitableview cell?

i got tired from trying all night to solve this issue at the begining my issue is when i add a new data it stored the data into NSMutableArray and in the cellForRowAtIndexPath i show the data, because i have sections so what i did was as follow
if(indexPath.section == 0)
{
for (NSDictionary *dictt in data) {
if ([[dictt objectForKey:#"Type"] isEqualToString:#"Electricity"]) {
NSLog(#"%#",[dictt objectForKey:#"Account"]);
cell.textLabel.text = [dictt objectForKey:#"Account"];
cell.detailTextLabel.text = [dictt objectForKey:#"Type"];
[mainTableView reloadInputViews];
}
}
}
When it comes to final result it show this first
when i add another data it show this
and if their is a data in another section let say water and when i delete that it starts deleting from Electricity section
and here is the delete function:
-(void)tableView:(UITableView *)tableView
commitEditingStyle:(UITableViewCellEditingStyle)editingStyle
forRowAtIndexPath:(NSIndexPath *)indexPath {
[data removeObjectAtIndex:indexPath.row]; [tableView
deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationTop];
[self saveData];
[mainTableView reloadData]; }
You are using the same array for all of your sections, so you definitely want to get a better data structure in place. In your delete function you aren't checking the section of the tableview, so that's why it's deleting from the wrong section.
Basically, you want to create an array that contains arrays of data for each section, so that in your master array the object at index 0 is the array for section 0 etc.

Add rows to existing UITableView section

I am trying to get some sample code on how I would add rows to an existing UITableView. I am trying to use the insertRowsAtIndexPaths: function.
[tableView insertRowsAtIndexPaths:addindexes withRowAnimation:UITableViewRowAnimationTop];
Any ideas how these indexes work and how I can add to an existing section or, if I don't have a section, then create a new section?
You have to create an array of indexpaths as -
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
for example if you want to insert 2nd row in section 0 and already have 1 row in section 0 then create an index path for row 1 in section 0 and call the insertRowsAtIndexPaths on table view, it will insert 2nd row in section.
If there is no section in table view then you should use an int for data source to give no of sections in table view, in this use -
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return noOfSection; //here section is as int which is initially zero
}
and initially your int "noOfSection" will be zero then there will be no section in table, then when you want to add section increase your int value by 1 and call [tableView reloadData];
You need to handle these functions if you are gonna insert row and section in a table view.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return noOfSections;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [[noOfRows objectForIndex:section] intValue];
}
also if you are updating your table view it is recommended that you use beginUpdates and endUpdates method.
this is how you insert new row and section.
noOfSections++;
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:3] withRowAnimation:UITableViewRowAnimationTop];
// update your array before calling insertRowsAtIndexPaths: method
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:1 inSection:2]]
withRowAnimation:UITableViewRowAnimationTop];
check the sample code provided by the apple.
http://developer.apple.com/library/ios/#DOCUMENTATION/UIKit/Reference/UITableView_Class/Reference/Reference.html