I have the expandable UITableView subclass. When a user clicks on a header then the table view removes all rows and only shows the section headers. If the user clicks again - all rows will be shown. The problem is: when the rows and sections counts become too large - table view is freezing and crash the user experience. I've tried to add each section in a different thread but still failed. The next code I've made is crashing when inserting rows with
he number of rows contained in an existing section after the update (23) must be equal to the number of rows contained in that section before the update (0), 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, 0 moved out).
But I never reload my table view don't change the counts in data source methods.
The code for expanding
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (int sec = 0; sec < [self.myCustomTableViewDataSource numberOfSectionsInTableView:self]; sec++) {
// NSString *threadNumber = [NSString stringWithFormat:#"inserting_thread_%tu", sec];
// dispatch_queue_t customQueue = dispatch_queue_create([threadNumber UTF8String], 0);
//
// dispatch_sync(customQueue, ^{
//
// });
[self.contractedSections removeObject:[NSNumber numberWithInteger:sec]];
NSMutableArray *indexPaths = [NSMutableArray new];
for (int i = 0; i < [self.myCustomTableViewDataSource resizableTableView:self numberOfRowsInSection:sec]; i++) {
[indexPaths addObject:[NSIndexPath indexPathForRow:i inSection:sec]];
}
dispatch_async(dispatch_get_main_queue(), ^{
[self insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationNone];
});
}
});
Related
I am working on an app where the user is able to add rows to the UITableView dynamically. This part is working fine. However, on my iOS screen, the UITableView is of a static height regardless of how many rows there are. Once the number of rows increases beyond a certain point, the user must scroll to see the additional cells.
What I would like to do is dynamically increase the height of the table with each row the user adds. So for example, if I have a tableView, if the user adds one row, I would like the tableview to be 50px tall, if the user adds 2 rows, the table dynamically increases in height to 100px, until it reaches say, a maximum height of 250px. My code for adding the rows dynamically, is as follows:
- (IBAction)addRow:(id)sender {
NSString *theObjectToInsert = [NSString stringWithFormat:#"Row #: %lu", (unsigned long)[self.tableData count]];
[self.tableData addObject:theObjectToInsert];
NSIndexPath *newPath=[NSIndexPath indexPathForRow:self.tableData.count-1 inSection:0];
[self.choiceTable insertRowsAtIndexPaths:#[newPath] withRowAnimation:UITableViewRowAnimationAutomatic];
[self.choiceTable scrollToRowAtIndexPath:newPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}
How do I incorporate the dynamic growth of the UITableView along with the increasing number of rows?
Just to be clear, when you say the table height is statically set right now I'll assume that is not desired and you do want it to be dynamic.
What if recommend doing is every time you add a row, check the height of all of the rows already added. You can do that by calling methods already implemented in the UITableViewDataSource protocol.
CGFloat maxDynamicTableHeight = 250.0f;
NSInteger numberOfSections = [self numberOfSectionsInTableView:self.tableView];
CGFloat runningHeight = 0.0f;
for (int section = 0; section < numberOfSections && runningHeight < maxDynamicTableHeight; section++) {
NSInteger numberOfRows = [self tableView:self.tableView numberOfRowsInSection:section];
for (int row = 0; row < numberOfRows && runningHeight < maxDynamicTableHeight; row++) {
runningHeight += [self tableView:self.tableView heightForRowAtIndexPath:[NSIndexPath indexPathForRow:row inSection:section]];
}
}
CGRect frame = self.tableView.frame;
frame.size.height = (runningHeight > maxDynamicTableHeight) ? maxDynamicTableHeight : runningHeight;
Self.tableView.frame = frame;
Sorry if there are any glaring issues, I'm on mobile so I can't test this right now.
I'm using the RATreeView component at https://github.com/Augustyniak/RATreeView by RafaĆ Augustyniak.
Using the posted /Demo code on github:
Add:
RADataObject *rdo = [self.data objectAtIndex:0];
[self.treeView expandRowForItem:[rdo.children objectAtIndex:0]];
at the bottom of viewWillAppear.
Change the lines of the phone objects to:
(void)loadData
{
RADataObject *phone2 = [RADataObject dataObjectWithName:#"Phone 2" children:nil];
RADataObject *phone3 = [RADataObject dataObjectWithName:#"Phone 3" children:nil];
RADataObject *phone4 = [RADataObject dataObjectWithName:#"Phone 4" children:nil];
RADataObject *phone1 = [RADataObject dataObjectWithName:#"Phone 1" children:[NSArray arrayWithObjects:phone2, phone3, phone4, nil]];
RADataObject *phone = [RADataObject dataObjectWithName:#"Phones"
children:[NSArray arrayWithObjects:phone1, nil]];
(This gives a nested list, where phone -> phone 1 -> phone2, phone3, phone4)
This reproduces the crash that I am having in my app.
It crashes on:
(void)expandCellForTreeNode:(RATreeNode *)treeNode withRowAnimation:(RATreeViewRowAnimation)rowAnimation
with an EXC_BAD_ACCESS for:
[self.tableView endUpdates];
I've traced through this for a couple of days, and am not sure how to work around this crash (or even why it is happening).
The error message I get using your code is the following. To reproduce this error, move
[self.treeView expandRowForItem:[rdo.children objectAtIndex:0]];
to viewDidAppear.
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 (24) must be equal to the number of
rows contained in that section before the update (12), plus or minus
the number of rows inserted or deleted from that section (24 inserted,
0 deleted) and plus or minus the number of rows moved into or out of
that section (0 moved in, 0 moved out).'
The number of rows has changed to 24, copying existing rows and insert them to the table, but the insertion is not successful.
It seems the thing you want to do is to expand a sub node, you can do that by looking at the implementation of didSelectRowAtIndexPath delegate method, because expanding a row is just like clicking a row. To do it manually add the following code.
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
RATreeNode *treeNode = [self.treeView treeNodeForIndexPath:indexPath];
[self.treeView expandCellForTreeNode:treeNode];
NSIndexPath *indexPath1 = [NSIndexPath indexPathForRow:1 inSection:0];
RATreeNode *treeNode1 = [self.treeView treeNodeForIndexPath:indexPath1];
[self.treeView expandCellForTreeNode:treeNode1];
It is just as if you click row 0, and row 1 sequentially. You also need to import this header file.
#import "RATreeView+Private.h"
Our app has a tableview that allows users to minimize/maximize its sections. There are also animations tied to hiding/showing rows depending on the user's input, as well as being connected to different devices (via Bluetooth 4.0). Because of the different sources of animations, we set up a scheduler to handle one animation at a time so that we didn't get any crashes tied to the number of rows/sections being out of sync after a [tableview endUpdates]. It's been working really well for our purposes.
However, a new section in our table has been giving me a bit of grief. Depending on the device that is connected to the iPad, the section either has 1 row or it has 8-9 rows. The rest of the rows have a height of 0. The section minimizes/maximizes just fine when it has 8-9 rows. When the section only has one row though, the insertRowsAtIndexPaths animation for the table does not complete until the section's row is off the screen. So unless the user changes tabs or scrolls down far enough for the table to push it off the screen, no other section can be minimized/maximized due to our scheduler checking first if it's already busy. Here is the code that gets called for the animations:
-(void)animateTableUsingAnimationType:(NSNumber*)aniType
{
static int animationID = -1;
if(tableAnimationBusy)
{
[self performSelector:#selector(animateTableUsingAnimationType:) withObject:aniType afterDelay:.05f];
}
else
{
tableAnimationBusy = true;
tat = [aniType intValue];
[CATransaction begin];
[CATransaction setCompletionBlock:^{
NSLog(#"Animation %d is complete!", tat);
tableAnimationBusy = false;
}];
//if-else if blocks checking for the animationID
//if open section 2
[self animateOpenTableSection:2]
else //undefined ID
tableAnimationBusy = false;
[CATransaction commit];
[self setUpLabelText];
}
}
and the animateOpenTableSection is:
-(void)animateOpenTableSection:(NSInteger)section
{
NSLog(#"Calling open on section: %d", section);
if (section == 2)
{
[self.tableView beginUpdates];
openFlagSettingsRowCount = fsr_COUNT; //max row count for section
[[MCUISectionHandler sharedInstance] sectionTouched:YES forSection:MCUISH_SECTION_NAME__FLAG];
NSMutableArray *indexPathsToInsert = [[NSMutableArray alloc] init];
for (NSInteger i = 0; i < fsr_COUNT; i++) {
[indexPathsToInsert addObject:[NSIndexPath indexPathForRow:i inSection:section]];
}
[self.tableView insertRowsAtIndexPaths:indexPathsToInsert withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
}
}
As you can see, I have debug statements in the animateOpenTableSection function, as well as the completion block in animateTableUsingAnimationType. The latter never gets called until the single row is off the screen. Any ideas as to why the tableview wouldn't let go of the CATransaction?
We were able to solve this problem and I forgot to post what I found out about our app. The reason the tableview wouldn't let go of the CATransaction was because of UIActivityIndicator subviews in the rows. They were being animated and so the CATransaction never ended because it was waiting for one or more UIActivityIndicators to stop animating.
So for those who may run into this problem, check to see if any subviews are being animated.
I am using TLIndexPathCollapsible.
Link: https://github.com/wtmoose/TLIndexPathTools
Now I have 2 sections (A and B), 3 rows for each section (a b c). If I expand A and B and I use this:
NSInteger rowNumber = 0;
for (NSInteger i = 0; i < indexPath.section; i++) {
rowNumber += [self tableView:tableView numberOfRowsInSection:i];
NSLog(#"number: %ld",(long)rowNumber);
NSLog(#"rows in previous section %ld",(long)[tableView numberOfRowsInSection:i]);
}
rowNumber += indexPath.row;
It works.
But when I collapsed section A and tap on (section B row a). Because the previous section (section A) is collapsed and has no rows therefore, it returns 0.
I am using one array to store.
How do i loop through section A and + 1 to get indexPath.row =3?
The idea with TLIPT is that you use a TLIndexPathDataModel to store your data items and then the TLIndexPathDataModel APIs let you easily convert indexPaths to data item and vise versa. So if you're using TLCollapsibleTableViewController, you'd do something like this:
id myDataItem = [self.indexPathController.dataModel itemAtIndexPath:indexPath];
If you need to find the index in an external array, then you can do this:
NSInteger index = [myArray indexOfObject:myDataItem];
In my application,I am performing reordering of cells but I am concatenating selected cells with each other
I have some cells and I am doing some cell animation i.e. user can select any cell and drag to any cell present in my table and after dragging these two cells are replaced by one cell that is created with these two cells.
But my problem is when I am trying to replace the last cell, it is not allowing me to do and the cell which I am trying to drag is added at the bottom without replacing the last cell and it is only happening for last cell
Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (50) must be equal to the number of rows contained in that section before the update (51), 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, 0 moved out).
2013-06-03 15:16:33.636 QuestionPro[6565:1d003] *** 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 (50) must be equal to the number of rows contained in that section before the update (51), 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, 0 moved out).'
Here is my code
- (void)endedDragGestureWithTranslationPoint:(CGPoint)translation {
[self updateFrameOfDraggedCellForTranlationPoint:translation];
self.draggedCell.layer.shouldRasterize = NO;
[(UITableViewCell *)self.draggedCell setHighlighted:NO animated:YES];
UITableViewCell *oldDraggedCell = [self.draggedCell retain];
NSIndexPath *blankIndexPath = [self.indexPathBelowDraggedCell retain];
CGRect blankCellFrame = oldDraggedCell.frame;
CGPoint blankCellCenter = {
.x = CGRectGetMidX(blankCellFrame),
.y = CGRectGetMidY(blankCellFrame)
};
NSIndexPath *rowToMoveTo = [tableView indexPathForRowAtPoint:blankCellCenter];
if ( rowToMoveTo ) {
CGRect rectForIndexPath = [tableView rectForRowAtIndexPath:rowToMoveTo];
if (dragGesture.state == UIGestureRecognizerStateFailed ){
rectForIndexPath = [tableView rectForRowAtIndexPath:self.indexPathBelowDraggedCell];
}
NSIndexPath * secIndex = [blankIndexPath copy];
[UIView animateWithDuration:0.25 delay:0 options:(UIViewAnimationOptionAllowUserInteraction|UIViewAnimationOptionBeginFromCurrentState) animations:^{
oldDraggedCell.frame = rectForIndexPath;
[self dragTableViewController:self hideDraggableIndicatorsOfCell:oldDraggedCell];
} completion:^(BOOL finished) {
[self dragTableViewController:self removeDraggableIndicatorsFromCell:oldDraggedCell];
[oldDraggedCell removeFromSuperview];
[oldDraggedCell release];
}];
if (dragGesture.state == UIGestureRecognizerStateEnded && secIndex.row != rowToMoveTo.row ){
[tableView beginUpdates];
[self joinTableViewCellsAtFirstRow:rowToMoveTo.row andSecondRow:secIndex.row];
NSArray * delArray = [[NSArray alloc] initWithObjects:secIndex , nil];
NSArray * rfrArray = [[NSArray alloc] initWithObjects:secIndex , nil];
[tableView deleteRowsAtIndexPaths:delArray withRowAnimation:UITableViewRowAnimationNone];
SALTableCellView * toggledCell = (SALTableCellView *)[tableView cellForRowAtIndexPath:rowToMoveTo];
if ( toggledCell.selectedImage && activateDelegate ){
[activateDelegate spotMarkedWithTitle:toggledCell.title andValue:toggledCell.shownValue andColor:[UIColor clearColor]];
}
[delArray release];
[rfrArray release];
[tableView endUpdates];
[self recalculateTapSumm];
[self refreshData];
}
else
hiddenCell.hidden = NO;
[secIndex release];
[tableView scrollRectToVisible:rectForIndexPath animated:YES];
}
[blankIndexPath release];
[oldDraggedCell release];
}
Well, there is a short answer and a long answer.
Short answer: You can't do what you are doing and i am surprised it works for the other rows.
Long answer: what you are trying to do goes against what a user would "normally" expect to happen. ie. if you "move" a row from one place to another, it would "move" and not combine. As the others have pointed out, the error message says as much. That the number of rows are changing from 51 to 50, as you are deleting the rows. the numbers must be the same when moving rows around.
solution: the solution could be quite simple, although i havent tried it myself. try firing all this delete and update tasks with a slight delay or something. that way, the moving rows would do its thingy and by which time the update task would be ready to remove the row and update the data so that you can show the consolidated row in the destination.
Check number of rows before and after you replace the cell.
The error showing you itself says that number of rows before replacing are not equal to the number of rows after replacement.