I m using Animation in table view cell...Animation is working fine when cell is totally visible.if any cell is partially visible at that time due to Animation my app is getting crashed at the line [_Mytableviewobject endUpdates];
Crash Log=Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Cell animation stop fraction must be greater than start fraction'
code section:
-(void)sectionHeaderView:(SectionHeaderView*)sectionHeaderView sectionOpened:(NSInteger)sectionOpened
{
//ENSLog(self, _cmd);
[_caseTable reloadData];
NSInteger countOfRowsToInsert = 1;
SectionInfo *sectionInfo = [self.sectionInfoArray objectAtIndex:sectionOpened];
sectionInfo.open = YES;
NSMutableArray *indexPathsToInsert = [[[NSMutableArray alloc] init] autorelease];
for (NSInteger i = 0; i < countOfRowsToInsert; i++)
{
[indexPathsToInsert addObject:[NSIndexPath indexPathForRow:i inSection:sectionOpened]];
}
NSMutableArray *indexPathsToDelete = [[[NSMutableArray alloc] init] autorelease];
NSInteger previousOpenSectionIndex = self.openSectionIndex;
if (previousOpenSectionIndex != NSNotFound)
{
SectionInfo *previousOpenSection = [self.sectionInfoArray objectAtIndex:previousOpenSectionIndex];
previousOpenSection.open = NO;
[previousOpenSection.headerView toggleOpenWithUserAction:NO];
NSInteger countOfRowsToDelete = 1;
for (NSInteger i = 0; i < countOfRowsToDelete; i++)
{
[indexPathsToDelete addObject:[NSIndexPath indexPathForRow:i inSection:previousOpenSectionIndex]];
}
}
// Style the animation so that there's a smooth flow in either direction.
UITableViewRowAnimation insertAnimation;
UITableViewRowAnimation deleteAnimation;
if (previousOpenSectionIndex == NSNotFound || sectionOpened < previousOpenSectionIndex)
{
insertAnimation = UITableViewRowAnimationTop;
deleteAnimation = UITableViewRowAnimationBottom;
}
else
{
insertAnimation = UITableViewRowAnimationTop;
deleteAnimation = UITableViewRowAnimationTop;
}
// Apply the updates.
[_caseTable beginUpdates];
[_caseTable deleteRowsAtIndexPaths:indexPathsToDelete withRowAnimation:deleteAnimation];
[_caseTable insertRowsAtIndexPaths:indexPathsToInsert withRowAnimation:insertAnimation];
[_caseTable endUpdates];
//ExNSLog(self, _cmd);
self.openSectionIndex = sectionOpened;
//ExNSLog(self, _cmd);
}
-(void)sectionHeaderView:(SectionHeaderView*)sectionHeaderView sectionClosed:(NSInteger)sectionClosed
{
//ENSLog(self, _cmd);
SectionInfo *sectionInfo = [self.sectionInfoArray objectAtIndex:sectionClosed];
sectionInfo.open = NO;
NSInteger countOfRowsToDelete = [_caseTable numberOfRowsInSection:sectionClosed];
if (countOfRowsToDelete > 0)
{
NSMutableArray *indexPathsToDelete = [[[NSMutableArray alloc] init] autorelease];
for (NSInteger i = 0; i < countOfRowsToDelete; i++)
{
[indexPathsToDelete addObject:[NSIndexPath indexPathForRow:i inSection:sectionClosed]];
}
[_caseTable deleteRowsAtIndexPaths:indexPathsToDelete withRowAnimation:UITableViewRowAnimationTop];
}
self.openSectionIndex = NSNotFound;
//ExNSLog(self, _cmd);
}
I experienced the same crash when trying to use a dummy footer to remove potential "empty" table view cells.
The solution was to get rid of
tableView:viewForFooterInSection:
tableView:heightForFooterInSection:
and replace them with the following, in viewDidLoad :
tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero];
Yes i also face this type of problem,do one thing just remove footer view.
it seems to happen when set tableview
-(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
-(CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section
while tableview style set Plain instead of Grouped style
I had the same problem after the ios 7 update : crash appeared during a "deleteRowsAtIndexPaths" for expand/collapse stuff in my table view.
Surprisingly, I have solved this issue by using heightForHeaderInsection instead of heightForFooterInSection.
-(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section{
return 1; // it was 0 before the fix
}
-(CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section{
return 0; // it was 1 before the fix
}
This issue has been reported (https://devforums.apple.com/message/795349).
You can use header of next section
my same answer is here
create empty section
use its header instead of footer
https://stackoverflow.com/a/12297703/788798
For people experienced this issue after iOS 7 updates:
Check the view frame of the section footer view. If it overlaps with the tableview cell. System throws this exception.
This is a bug in iOS. I filed a bug report and they came back saying its a duplicate of 14898413. This bug is still marked as open in apple bug reporter.
Options for fixes:
1) Remove footer
2) Instead of deleting rows, begin and end updates reload the table
I was getting the same error and crash whenever I would scroll to the bottom of the tableview and try to delete cells and insert sections my solution was to scroll the tableview back to the top before calling [tableview beginsUpdates] using the tableviews content offset. With this solution I didn't have to change my section header or footer at all.
[self.tableView setContentOffset:CGPointZero animated:NO];
I solved this bug by calling reloadData() for last section:
func expandSectionPressed(sender: UIButton) {
let object = items[sender.tag]
object.expanded = !object.expanded
sender.selected = object.expanded
var indexPaths = [NSIndexPath]()
for i in 0..<7 {
indexPaths.append(NSIndexPath(forRow: i, inSection: sender.tag))
}
if object.expanded {
tableView.insertRowsAtIndexPaths(indexPaths, withRowAnimation: .Fade)
} else {
// strange crash when deleting rows from the last section //
if sender.tag < numberOfSectionsInTableView(tableView) - 1 {
tableView.deleteRowsAtIndexPaths(indexPaths, withRowAnimation: .Fade)
} else {
tableView.reloadData()
}
}
}
I experienced the same problem - exception 'Cell animation stop fraction must be greater than start fraction' in [[self tableView] endUpdates]; I solved it by catching the exception and making the endUpdates second time.
[[self tableView] beginUpdates];
#try
{
[[self tableView] endUpdates];
}
#catch (NSException* exception)
{
[[self tableView] endUpdates];
}
Related
Came across this post but did not fix my problem.
iPhone app crashing on [self.tableView endUpdates]
Having a UITableView which loads articles from a newspaper website. First load works
as expected but when I use an UIRefreshControl to fetch the articles again my app crashes
when (animating) inserting rows.
Error:
Code:
- (void)insertRowsWithAnimation
{
NSMutableArray *indexPaths = [NSMutableArray array];
NSInteger i = self.latestArticlesArray.count - self.latestArticlesArray.count;
for (NSDictionary *dict in self.latestArticlesArray) {
[indexPaths addObject:[NSIndexPath indexPathForRow:i inSection:0]];
i++;
}
[self.tableView beginUpdates];
[self.tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationMiddle];
[self.tableView endUpdates];
}
- (void)fetchEntries
{
UIView *currentTitleView = [[self navigationItem] titleView];
UIActivityIndicatorView *aiView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
[[self navigationItem] setTitleView:aiView];
[aiView startAnimating];
void (^completionBlock) (NSArray *array, NSError *err) = ^(NSArray *array, NSError *err) {
if (!err) {
[[self navigationItem] setTitleView:currentTitleView];
self.latestArticlesArray = [NSArray array];
self.latestArticlesArray = array;
[self insertRowsWithAnimation];
}
};
[[Store sharedStore] fetchArticlesWithCompletion:completionBlock];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.latestArticlesArray.count;
}
If you want to see more methods please let me know. Hope you can help. From what I've learned so far I think the number of posts have changed therefore the table expects another amount of articles to show?
The line
NSInteger i = self.latestArticlesArray.count - self.latestArticlesArray.count;
sets i to zero, therefore insertRowsAtIndexPaths is called with an empty array.
I assume that your intention was to call insertRowsAtIndexPaths with the row numbers of the newly added rows, but then you have to remember the old data before replacing the array in
self.latestArticlesArray = array;
But note that since you replace the entire array, you can as well call
[self.tableView reloadData];
instead of beginUpdates/insertRowsAtIndexPaths/endUpdates.
Update: My first analysis is wrong (and wattson12's is correct). As you said in the comments, you need just a simple animation that removes all previous rows and inserts the new rows after a fetch. This can be done like this:
- (void)replaceArticlesWithAnimation:(NSArray *)articles
{
NSUInteger oldCount = [self.latestArticlesArray count];
self.latestArticlesArray = articles;
NSUInteger newCount = [self.latestArticlesArray count];
[self.tableView beginUpdates];
for (NSUInteger i = 0; i < oldCount; i++) {
[self.tableView deleteRowsAtIndexPaths:#[[NSIndexPath indexPathForRow:i inSection:0]] withRowAnimation:UITableViewRowAnimationAutomatic];
}
for (NSUInteger i = 0; i < newCount; i++) {
[self.tableView insertRowsAtIndexPaths:#[[NSIndexPath indexPathForRow:i inSection:0]] withRowAnimation:UITableViewRowAnimationAutomatic];
}
[self.tableView endUpdates];
}
and in fetchEntries you call
if (!err) {
[[self navigationItem] setTitleView:currentTitleView];
[self replaceArticlesWithAnimation:array];
}
The reason it loads the first time is because you are inserting a number of rows each time, so on the first run the number of rows changes from 0 to the count in array (in the completion block), and you call insertRows with a number of index paths equal to the count of the array.
The 2nd time you call it you are inserting new rows, but you are not updating the count to reflect the new sum. You should be adding your existing array to the one returned in the completion block, and returning the count of that combined array in numberOfRowsInSection
dispatch_async(dispatch_get_main_queue(), ^(void){
//Run UI Updates
});
in my tableview no of rows in section method is called and it returns value 17,but the cellforrowatindexpath is not getting called.i have put breakpoints in the first line of this method but this point is never shown when debugging,i have followed the tableviewdelegate and datasource.and the tableviews datasource,delegate are properly set in the Int builder.
i am also posting some of the code
in my viewcontroller.m
-(void)viewDidLoad
{
tweets=[[NSMutableArray alloc]init];
[self updateStream:nil];
[self.tweetsTable setDelegate:self];
[self.tweetsTable setDataSource:self];
}
-(NSInteger)tableView:(UITableView *)tweetsTable numberOfRowsInSection:(NSInteger)section {
return [tweets count];
}
-(UITableViewCell *)tableView:(UITableView *)tweetsTable cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"twitCell";
TwitCell *cell = (TwitCell *)[tweetsTable dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = (TwitCell *)[[[TwitCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
cell.tweet = [tweets objectAtIndex:indexPath.row];
[cell layoutSubviews];
return cell;
}
cellForRowAtIndexPath won't get called if your tableview has height of 0
make sure your tableview always has VISIBLE rows
CGRect frame = self.tableView.frame;
frame.size.height = 100;
self.table.frame = frame;
It will not get called if you are returning 0 rows in numberOfRowsInSection method.
Please check the number of rows that you return.
Given the code you've shown us there only a few possibilities. Either self.tweetsTable is nil, tweets is nil, or tweets contains no element and count is returning zero. Now I know you say that everything is correct, but clearly something is up! You can add a bit of defensive code to detect these problems.
-(void)viewDidLoad
{
tweets=[[NSMutableArray alloc]init];
[self updateStream:nil];
NSAssert(self.tweetsTable, #"self.tweetsTable must not be nil.");
[self.tweetsTable setDelegate:self];
[self.tweetsTable setDataSource:self];
}
-(NSInteger)tableView:(UITableView *)tweetsTable numberOfRowsInSection:(NSInteger)section {
NSAssert(tweets, #"tweets must not be nil here");
NSUInteger n = [tweets count];
if(n == 0)
NSLog(#"WARNING: %# returning 0", __PRETTY_FUNCTION__);
return (NSInteger)n;
}
If you do this and one of the asserts fires you'll know where your problem is. If no assert fires then something is going on outside the scope of the code you have shown (e.g. something is getter released to soon or memory getting clobbered). Oh and one final thing -- can you see the empty table view on the screen? If you table is not visible AFAIK cellForRowAtIndexPath won't be called.
check this
or else use viewDidLoad code in viewWillAppear
Here is my function. After I placed "dispatch_async(dispatch_get_main_queue()..." inside of "tweets = [NSJSONSerialization.....", it works.
tweets = [NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingMutableLeaves error:&jsonError];
if (tweets) {
// We have an object that we can parse
NSLog(#"%#", tweets);
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
}
else {
// Inspect the contents of jsonError
NSLog(#"%#", jsonError);
}
In my cases I had created a UITableView as a property on my view controller, but I forgot to add it as a subview to self.view
Strangely you will get the symptoms that sujith described: numberOfRowsInSection will be called, but cellForRowAtIndexPath will not!
This was my missing line:
[self.view addSubView:self.myTableViewProperty];
And to defend against it:
NSAssert(self.myTableViewProperty.superview != nil, #"The table view dose not have a superview");
[self.myTableViewProperty reloadData];
You can refer following code to reload your tableView
[self.tableView performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:NO];
In my case I was using custom subclass of UITableView and I have not called super super.layoutSubviews()
override func layoutSubviews() {
super.layoutSubviews() // Forgotten to call super implementation
self.layer.borderColor = Constants.primaryColor.cgColor
self.layer.borderWidth = Constants.primaryBorderWidth
self.indicatorStyle = .white
}
Ok so I am working on implementing a UISearchBar on a tableView that has sections. This may be wrong, but to populate the table view the first time, I have an array with lots of entries, and then populate the sections like this:
if(indexPath.section ==0){
[cell.textLabel setText:[tableData objectAtIndex:indexPath.row]];
}else if(indexPath.section ==1){
[cell.textLabel setText:[tableData objectAtIndex:indexPath.row+4]];
}else if(indexPath.section ==2){
[cell.textLabel setText:[tableData objectAtIndex:indexPath.row+8]];
}
Which is far from elegant, but it works. Now I am trying to hookup the UISearchBar, and this is the method that I am running into issues with:
- (void)searchBar:(UISearchBar *)sBar textDidChange:(NSString *)searchText
{
[tableData removeAllObjects];// remove all data that belongs to previous search
if([searchText isEqualToString:#""] || searchText==nil){
[tableView reloadData];
return;
}
NSInteger counter = 0;
for(NSString *name in dataSource)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
NSRange r = [name rangeOfString:searchText];
if(r.location != NSNotFound)
{
[tableData addObject:name];
}
[pool release];
}
[tableView reloadData];
}
So I am making an array again of entries that fit the search criteria, but then when I am trying to reload my tableView, it gets all bungled up because it is expecting sections. But all I want is the results in just a plain section-less tableView.
How can I implement this UISearchBar with a tableView with sections?
Thanks
set a BOOL when you enter search and adjust your section count accordingly
e.g.
in viewDidLoad
BOOL isSearching = NO;
set to YES when you enter the textDidChange method.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
int t;
if (isSearching) t = 1
else {
t= array.count;
}
return t;
}
You don't need to keep another variable around; just interrogate the tableView argument to see who is asking for the number of sections. For example, suppose your data is available in a fetchedResultsController:
if (tableView == self.searchDisplayDisplayController.searchResultsTableView) {
// your table is the search results table, so just return 1
return 1;
} else {
// your table is your "own" table
return [[self.fetchedResultsController sections] count];
}
I do the same thing in many of my table view delegate and data source methods.
I added a button on the uinavigationbar I want to use it to clear all the rows of uitablview
How can I do that?
A bit late... but try this:
Where you want to clear the table:
// clear table
clearTable = YES;
[table reloadData];
clearTable = NO;
This function should look like:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if(clearTable)
return 0;
(...)
}
There should be a UITableView delegate method that populates the UITableView control:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
Find this method and change where the data for each cell is being pulled from. If you are currently using the indexPath to access an array of values to populate your tableView cells, you can programmatically switch to an array of empty values, or even just return empty cells from this method when the condition is right.
Simple.. set your data source (NSArray or NSDictionary) to nil and [self.tableView reloadData]!
What do you exactly mean by clearing the row? Do you still want them to be there, but without text? If yes, here's this code:
UITableViewCell *cell;
NSIndexPath *index;
for (int i = 0 ; i < count; i++) {
index = [NSIndexPath indexPathForRow:i inSection:0];
cell = [tableView cellForRowAtIndexPath:index];
cell.textLabel.text = #"";
}
If you want to delete them, you can use this code:
[uiTable deleteRowsAtIndexPaths:[NSArray arrayWithObject:index] withRowAnimation:UITableViewRowAnimationFade];
Before you are reloading the table remove all objects from your tableView array (The array which populated your tableView) like so:
[myArray removeAllObjects];
[tableView reloadData];
The crash is occurring because you need to reset number if rows count in the -
-(NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section
{
return [tempArray count];
}
one way is use a flag variable as below:
-(NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section
{
if (deleting)
return rowcount;
else
return [tempArray count];
}
Also you need to modify the deleting code as below:
deleting = YES;
for (i = [uiTable numberOfRowsInSection:0] - 1; i >=0 ; i--)
{
NSIndexPath *index = [NSIndexPath indexPathForRow:i inSection:0];
[uiTable deleteRowsAtIndexPaths:[NSArray arrayWithObject:index] withRowAnimation:UITableViewRowAnimationFade];
rowcount = i;
}
deleting = NO;
[uiTable reloadData];
in your .h file
BOOL deleting;
NSInteger rowcount;
Hope this helps...
The rowcount = i should go before the call.
[uiTable deleteRowsAtIndexPaths:[NSArray arrayWithObject:index]
withRowAnimation:UITableViewRowAnimationFade];
Otherwise, it will crash.
tableView.dataSource = nil
tableView.reloadData()
I have a problem with animating deletes and inserts on a UITableView. The Fact ist, that I have different cell heights, some 44.0 some 135.0 now. I have uitableviewstylegrouped with different sections. The first row of each sections is a grouping row. On click I remove all rows of this section except the grouping row. But that animation looks weird, when animating (UITableViewRowAnimationTop) the 135px height cell. I tried to set self.tableview.cellheight to 135 and commented out the tableView:cellHeightForIndexPath-Method. And the Animation works fine. Or I set every cellheight to 135 in tableView:cellHeightForIndexPath-Method.
It looks like the animation process checks the height of the first row in the sections an takes that height of the cell for all following cells to animate.
Somebody has an idea?
- (void) showhideGroup:(int)group
{
NSMutableArray *indexPaths = [[NSMutableArray alloc] init];
MCGroup *handleGroup = [groups objectAtIndex:group];
NSMutableArray *groupRows = [visibleGroupData objectForKey:handleGroup.title];
[self.tableView beginUpdates];
if (!handleGroup.hidden) {
int row = 0;
for(MobileFieldMapping *field in groupRows)
{
if (![field.datatype matchesInsensitive:GROUPING_CELL])
{
NSIndexPath *path = [NSIndexPath indexPathForRow:row inSection:group];
[indexPaths addObject:path];
}
row++;
}
row = 0;
for(NSIndexPath *index in indexPaths)
{
[groupRows removeObjectAtIndex:index.row-row];
row++;
}
[self.tableView deleteRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationTop];
handleGroup.hidden=YES;
}
else
{
NSMutableArray *allGroupRows = [groupData objectForKey:handleGroup.title];
int row = 0;
for (MobileFieldMapping *field in allGroupRows)
{
if (![groupRows containsObject:field])
{
NSIndexPath *path = [NSIndexPath indexPathForRow:row inSection:group];
[indexPaths addObject:path];
[groupRows insertObject:field atIndex:row];
}
row++;
}
[self.tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationTop];
handleGroup.hidden=NO;
}
[self.tableView endUpdates];
[indexPaths release];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [self tableView:tableView cellForRowAtIndexPath:indexPath];
if ([cell isKindOfClass:[FormCell class]])
{
return [(FormCell*)cell height];
}
return 44.0;
}
I had the same problem.
My workaround is to use fade section animation:
[tableView reloadSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:UITableViewRowAnimationFade];
Instead of one for elements:
[tableView deleteRowsAtIndexPaths:tmpArray withRowAnimation:UITableViewRowAnimationTop];
This looks much smoother.
An old one that is still unanswered. I assume you figured it out a long time ago, but here is my first thought. (I recall trying to do something like this myself.)
When the UITableView calls heightForRowAtIndexPath:, it will be because the table is trying to prepare the cell, so you can't use the cell to return the height. This may cause an infinite regression in calls or it may detect this and just give up or throw an exception and leave you holding the bag.
You must calculate the cell height without using the cell itself.
As for the cell height assumption, try calling insert/delete for individual cells rather than a single call to do it all at once. The UITableView will still batch them up for the animation after you call endUpdates.