UITableView add cell Animation - ios

Can any one help me out with UITableView animating issue?
By default we have animation for deleting cell and reordering cells in UITableView.
Can we have animated adding cell, if so how to do it.
I have checked out Three20, did not not get how twitter has done the table expand animation under MyProfile>ReTweets.
Want to try it without Three20 frameowrk, using the existing animation in UITableView.

You can use the following UITableView method:
- (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation
Example with self.dataSource being a mutable array and your table only having 1 section:
[self.dataSource addObject:#"New Item"];
NSIndexPath *newIndexPath = [NSIndexPath indexPathForRow:[self.dataSource count]-1 inSection:0];
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
Note: Subtracted 1 from datasource count since NSIndexPath is zero indexed.

Swift
Example array that is the data source for the table view
var myArray = ["Cow", "Camel", "Sheep", "Goat"]
The following will add a row with animation at the top of the table view.
// add item to current array
myArray.insert("Horse", atIndex: 0)
// insert row in table
let indexPath = NSIndexPath(forRow: 0, inSection: 0)
tableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Fade)
Multiple updates
If you need to do multiple insertions and/or deletions then surround them with beginUpdates() and endUpdates().
tableView.beginUpdates()
tableView.insertRowsAtIndexPaths([addIndexPath1, addIndexPath2, ...], withRowAnimation: .Fade)
tableView.deleteRowsAtIndexPaths([deleteIndexPath1, deleteIndexPath2, ...], withRowAnimation: .Fade)
tableView.endUpdates()
Further reading
Documentation

Use reloadRowsAtIndexPaths:indexPath
tableView.beginUpdates()
[tableView reloadRowsAtIndexPaths:indexPath withRowAnimation:UITableViewRowAnimationAutomatic];
tableView.endUpdates()
i hope this will help you..

In swift is also possible using:
func insertRow(entries: [String]) {
tableView.performBatchUpdates({
self.tableView.insertRows(at: [IndexPath(row: entries.count - 1, section: 0)], with: .bottom)
}, completion: nil)
}

Related

Assertion failure in -[UITableView _endCellAnimationsWithContext:] when converting Objective-C to Swift

I'm having a huge issue with some I code I've converted to Swift. When calling self.tableView.endUpdates() my table view stays empty and I get the following error:
Assertion failure in -[UITableView _endCellAnimationsWithContext:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKit_Sim/UIKit-3512.60.7/UITableView.m:1501
Weirdly enough when the same code was written in Objective-C this error is not showing up.
The table view datasource and delegate ar still in Objective-C and used by many other view controllers in the project.
Here is the code in Objective-c:
self.dataSource.itemArray = [self.userList copy];
NSMutableArray *indexPaths = [NSMutableArray new];
for (NSUInteger i = (offset); i < (end); i++) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0];
[indexPaths addObject:indexPath];
}
[self.tableView beginUpdates];
[self.tableView insertRowsAtIndexPaths:[indexPaths copy]
withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
And this is the same code but then in swift:
self.dataSource.itemArray = self.userList.copy() as! [User]
var indexPaths = [NSIndexPath]()
for i in offset...end {
let indexPath = NSIndexPath(forRow: i, inSection: 0)
indexPaths.append(indexPath)
}
self.tableView.beginUpdates()
self.tableView.insertRowsAtIndexPaths(indexPaths, withRowAnimation: .Fade)
self.tableView.endUpdates()
The tableView:numberOfRowsInSection: is called correctly and return the same in both case. Also the cell are correctly registered.
Is there a know issue or is ia just that I can't extent an Objective-C base class in Swift.
In this piece of code:
for i in offset...end {
let indexPath = NSIndexPath(forRow: i, inSection: 0)
indexPaths.append(indexPath)
}
The last indexPath which will be added is NSIndexPath(forRow: end, inSection: 0), that means that you are adding one extra row to your table view which is not expected.
Probably you want to change your loop to:
for i in offset..<end {
let indexPath = NSIndexPath(forRow: i, inSection: 0)
indexPaths.append(indexPath)
}
In my case
myTableView.reloadSections([sectionIndex], with: .none)
did work and removed the error. It may be due to any button or textfield tag getting wrong index after row deletion.

get the IndexPath.row and IndexPath.section Before Creation of the table View

I want to update a particular UITableViewCell into the Swift IOS.
"reloadRowsAtIndexPaths" is used for updating the particular UITableViewCell,
self.tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.None)
My Concern is "How to get the IndexPath.row and IndexPath.section" ([indexPath] for this Case ) before creating the UITableView , i want to pass both to update the particular UItableViewCell.
tableView.reloadRowsAtIndexPaths([NSIndexPath(forRow: 0, inSection: 0)], withRowAnimation: .None)
With this you can init a NSIndexPath variable. [] brackets because reloadRowsAtIndexPath wants an array of those as parameter.
Parameters in NSIndexPath initializer are pretty straight forward i guess (forRow --> which row | inSection --> which section
hf ;)

Reload tableview section without scroll or animation

I am reloading a tableView section using this code -
self.tableView.beginUpdates()
self.tableView.reloadSections(NSIndexSet(index: 1), withRowAnimation: UITableViewRowAnimation.None)
self.tableView.endUpdates()
Still the rows replacement is animated and the table view scrolls to the top.
How can I make sure that no animation will be applied to the section reload + prevent the table view from scrolling.
To prevent scrolling and animation, do this:
UIView.performWithoutAnimation {
let loc = tableView.contentOffset
tableView.reloadRows(at: [indexPath], with: .none)
tableView.contentOffset = loc
}
Swift 4:
Actually, the best way to prevent crazy animation during scrolling or expand/collapse cells is:
UIView.performWithoutAnimation {
self.tableView.reloadRows(at: [indexPath], with: .none)
}
Try this:
UIView.setAnimationsEnabled(false)
self.tableView.beginUpdates()
self.tableView.reloadSections(NSIndexSet(index: 1) as IndexSet, with: UITableViewRowAnimation.none)
self.tableView.endUpdates()
Swift 5
UIView.performWithoutAnimation {
self.tableView.reloadRows(at: [indexPath], with: .none)
}
Neither of given answers worked for me.
Here's the solution I found, hope it would be useful to someone
Swift 4:
let lastScrollOffset = tableView.contentOffset
tableView.beginUpdates()
tableView.reloadSections(IndexSet(integer: 1), with: .none)
tableView.endUpdates()
tableView.layer.removeAllAnimations()
tableView.setContentOffset(lastScrollOffset, animated: false)
Add one line more to this method in viewDidLoad
self.tableView.remembersLastFocusedIndexPath = true
Add this code where you want refresh after updation
UIView.setAnimationsEnabled(false)
self.tableView.beginUpdates()
self.tableView.reloadSections(NSIndexSet(index: 1) as IndexSet, with: UITableViewRowAnimation.none)
self.tableView.endUpdates()
Objective-C:
[UIView setAnimationsEnabled:NO];
[[self tableView]beginUpdates];
[self.tableView reloadSections:[[NSIndexSet alloc] initWithIndex:1] withRowAnimation:UITableViewRowAnimationNone];
[[self tableView]endUpdates];
But I think the following code is more useful and better.
[UIView performWithoutAnimation:^{
[[self tableView]beginUpdates];
[self.tableView reloadSections:[[NSIndexSet alloc] initWithIndex:1] withRowAnimation:UITableViewRowAnimationNone];
[[self tableView]endUpdates];
}];
I've had this problem too. It seems to stem from putting an IndexPath inside .reloadSections instead of a IndexSet as it requests. Which is why it seems to work with .reloadRows with out issue:
tableView.reloadRows(at: [IndexPath], with: UITableView.RowAnimation)
tableView.reloadSections(IndexSet, with: UITableView.RowAnimation)
Just wrap the section you want to reload as an IndexSet:
tableView.reloadSections(IndexSet(integer: sectionInt), with: .none)

Animate UITableViewCell gives me error

I have a UITableView. I want to add a cell to the beginning of the tableView with animation. I tried the following:
let indexPath = NSIndexPath(forRow: 0, inSection: 0)
tableView.reloadRowsAtIndexPaths(indexPath, withRowAnimation: UITableViewRowAnimation.Automatic)
But I get the following error:
Cannot invoke 'reloadRowsAtIndexPaths' with an argument list of type
'(NSIndexPath!, withRowAnimation: UITableViewRowAnimation)'
(When I just do tableView.reloadData() it works fine.)
What am I doing wrong, and how can I fix it?
tableView.reloadRowsAtIndexPaths expects an [NSIndexPath] instead of an NSIndexPath! as first argument.
so fix it by calling
let indexPath = NSIndexPath(forRow: 0, inSection: 0)
let indexPaths = [indexPath]
tableView.reloadRowsAtIndexPaths(indexPaths, withRowAnimation: UITableViewRowAnimation.Automatic)
You get the error, because reloadRowsAtIndexPaths expects an array of index paths, not a single index path. However reloadRowsAtIndexPaths wouldn't add a cell, but just reload the cells at the specified index paths.
Instead you should use insertRowsAtIndexPaths to add your cell.

Better way to loop through array and reload data?

I'm trying to only display items that meet the criteria ( if location <= searchedDistance) but it seems like this is a very inefficient way to display this data. Does this function reload the entire tableview after each location is checked? If so, isn't this a horrible use of memory/time?
What's a better alternative?
var locations = [Int: Int]()
func searchSuccessful() {
var row: Int = 0
var section: Int = 0
for (index, location) in locations {
if location <= searchedDistance {
self.theTableView.beginUpdates()
let indexPath = NSIndexPath(forRow: row, inSection: section)
self.theTableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
self.theTableView.endUpdates()
}
}
}
Put the begin/end update outside of the loop.
self.theTableView.beginUpdates()
// Do all the changes in a single place, surrounded by begin/end updates
for (index, location) in locations {
if location <= searchedDistance {
let indexPath = NSIndexPath(forRow: row, inSection: section)
self.theTableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
}
}
self.theTableView.endUpdates()
Swift
self.tableView.beginUpdates()
let indexPath = NSIndexPath(forRow: row, inSection: section)
self.tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
self.tableView.endUpdates()
Obj-C
[self.tableView beginUpdates];
NSIndexPath * indexPath = [NSIndexPath indexPathForRow:row inSection:section];
[self.tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];

Resources