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"
Related
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];
});
}
});
I have an app that used to work before I upgraded to XCODE 5. I needed to find the row that was selected for a structure I was displaying which has over 100 rows so obviously can be scrolled on the display. The code that used to work is:
NSIndexPath *indexPath = [self.mainTableView indexPathForCell:myCell];
NSInteger row = [indexPath row];
Now, regardless of the row selected, the value of row is always 0. Anyone have any suggestions for me? Thanks.
This worked for me...
CGPoint pos = [pressedButton convertPoint:CGPointZero toView:self.mainTableView];
NSIndexPath *indexPath = [self.mainTableView indexPathForRowAtPoint:pos];
NSInteger row = [indexPath row];
Remember that in Objective-C, you can always send a message nil, and methods that return something (i.e., not void) will return either nil(for an object) or 0 (for a value) when sent to nil (for structs, it is more complicated; read this).
So make sure that:
self.mainTableView is not nil
indexPath is not nil
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.
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.
Targeting iOS 5.0 I see SIGABRT when I try to access row or section properties of NSIndexPath object in the following code:
NSIndexPath* path = [[NSIndexPath alloc] initWithIndex:0];
int row = path.row; //SIGABRT
int section = path.section;//SIGABRT
This example suggests using indexPathWithIndex: instead http://developer.apple.com/library/mac/#samplecode/SourceView/Listings/BaseNode_m.html#//apple_ref/doc/uid/DTS10004441-BaseNode_m-DontLinkElementID_6
path = [NSIndexPath indexPathWithIndex:0];
The results remain consistent. Does anyone have an explanation of why this might happen? Is it that we just cannot operate on NSIndexPath in iOS 5.0?
Thanks in advanced.
Try creating the index path using indexPathForRow:inSection:
NSIndexPath* path = [NSIndexPath indexPathForRow:0 inSection:0];
The only thing I can think of is to make sure you have UIKit.framework as one of your frameworks. You can do this by pressing the plus button in the "Link Binary With Libraries" section under "Build Phases" on your target. Also, make sure that you import NSIndexPath.h in the file you're trying to access the index path from.
I've run into this problem before, and this solved it. That's because although NSIndexPath is universal between Cocoa and CocoaTouch, the Cocoa version doesn't have properties row and section.
If you already have UIKit.framework, try using [NSIndexPath indexPathForRow:row inSection:section] instead of [NSIndexPath indexPathWithIndex:index].
I think the problem is that indexPathWithIndex: creates a one-node index path. Same for initWithIndex:. The section and row properties come from the UITableView category to the NSIndexPath class. A one-node index path is invalid for use with a table view because index paths passed to table views must contain two indices specifying the section and row (according to the error message I get when I run your code). The following code creates an index path with two indices and runs without throwing an NSInternalInconsistencyException:
NSUInteger x[] = {0 , 0};
NSIndexPath *path = [[NSIndexPath alloc] initWithIndexes: x length: 2];
int row = [path row];
int section = [path section];
row = path.row;
section = path.section;
Is this what you meant by "operate on NSIndexPath in iOS 5.0"?