Better way to loop through array and reload data? - ios

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];

Related

MoveRow method not animating when moving between sections

Im creating Shopping List app. I use 2 sections for separating bought items and Items that are not bought. I use moveRow method for moving rows between said 2 sections. This is the code for moving rows.
if indexPath.section == 0 {
self.shoppingItems.remove(at: indexPath.row)
self.shoppingItemsBought.append(item)
self.tableView.beginUpdates()
let fromIndexPath = NSIndexPath(row: indexPath.row, section: 0)
let toIndexPath = NSIndexPath(row: 0, section: 1)
self.tableView.moveRow(at: fromIndexPath as IndexPath, to: toIndexPath as IndexPath)
self.tableView.endUpdates()
} else {
self.shoppingItemsBought.remove(at: indexPath.row)
self.shoppingItems.append(item)
self.tableView.beginUpdates()
let fromIndexPath = NSIndexPath(row: indexPath.row, section: 1)
let toIndexPath = NSIndexPath(row: 0, section: 0)
self.tableView.moveRow(at: fromIndexPath as IndexPath, to: toIndexPath as IndexPath)
self.tableView.endUpdates()
}
The rows change their location but without animation. What am I missing?
One option is to use CATransaction around the moveRow. Put your changes inside the CATransaction block and on completion, force the reload. This will do the reload with animations.
Example:
CATransaction.begin()
CATransaction.setCompletionBlock {
tableView.reloadData()
}
tableView.beginUpdates()
// Update the datasource
if indexPath.section == 0 {
self.shoppingItems.remove(at: indexPath.row)
self.shoppingItemsBought.append(item)
}
else {
self.shoppingItemsBought.remove(at: indexPath.row)
self.shoppingItems.append(item)
}
// Get new IndexPath (indexPath is the old one, as in current)
let newIndexPath = IndexPath(row: 0, section: indexPath.section == 0 ? 1 : 0)
// Move the row in tableview
tableView.moveRow(at: indexPath, to: newIndexPath)
tableView.endUpdates()
CATransaction.commit()

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.

How to add last row to UITableView with smooth scrolling?

I'm building chat interface using UITableView. i'm adding cells using below code
self.tableView.beginUpdates()
self.tableView.insertRows(at: [IndexPath(row: self.messageCards.count - 1, section: 0)], with: .bottom)
self.tableView.endUpdates()
and after adding i'm using scrollToRowAtIndexPath to show last inserted row.
let indexPath = IndexPath(row: self.messageCards.count - 1, section: 0)
self.tableView.scrollToRow(at: indexPath, at: .bottom, animated: false)
but at the end both animation are mixing and scrolling not happening smoothly.
You can try this, I have tested the code:
arrayData.addObject(str)
let indexpath = NSIndexPath(forRow: arrayData.count-1, inSection: 0)
tblView.insertRowsAtIndexPaths([indexpath], withRowAnimation: UITableViewRowAnimation.Automatic)
tblView.scrollToRowAtIndexPath(indexpath, atScrollPosition: UITableViewScrollPosition.Bottom, animated: true)
Following is output: let me know if you want complete source.
Please check out this.
[UITableView animateWithDuration:5.0 delay:0.1 options:UIViewAnimationOptionCurveEaseOut animations:^{
[self.tableView insertRowsAtIndexPaths:#[[NSIndexPath indexPathForRow:self.messageCards.count-1 inSection:0]] withRowAnimation:UITableViewRowAnimationFade];
} completion:^(BOOL finished) {
NSLog(#"animated");
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathWithIndex:self.messageCards.count-1] atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}];
Its wonder, but to achieve smooth adding when scrolling just need to call reloadData()
func fetchNext(_ countToFetch: Int? = 10) {
let existingCount = items?.count ?? 0
networkService?.items(bySkip: existingCount, take: countToFetch, completion: { (data, error) in
if let newItems = data?.items() as? [Element], newItems.count > 0 {
self.items?.append(contentsOf: newItems)
self.refreshCallback?()
}
})
}
Callback is:
datasource?.refreshCallback = { [weak self] in
if #available(iOS 10.0, *) {
self?.table.refreshControl?.endRefreshing()
}
self?.table.reloadData()
}
[self.messageCards addObject:#"DATA Whatever you want"];
[self.tableView reloadData];
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:self.messageCards.count-1 inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:YES];

insert row at index path call scroll to top table view?

When i insert row at index path in uitableview, then my tableview scroll to top? Why?
let indexPathForCell = NSIndexPath(forRow: 5, inSection: 1)
tableView.beginUpdates()
tableView.insertRowsAtIndexPaths([indexPathForCell], withRowAnimation: .Automatic)
tableView.endUpdates()
All code that is invoked during the addition of the cell
func buttonDidPressed(button: CheckMarkView) {
let indexPathForCell = NSIndexPath(forRow: 5, inSection: 1)
buttonPressedTag = button.tag
for checkMark in buttons {
if checkMark.tag == buttonPressedTag {
if buttonPressedTag == 4 {
checkMark.show()
checkMark.userInteractionEnabled = false
cellWithCategories["Recomendation"]?.append("slideCell")
tableView.beginUpdates()
tableView.insertRowsAtIndexPaths([indexPathForCell], withRowAnimation: .None)
tableView.endUpdates()
}
checkMark.show()
} else {
if (tableView.cellForRowAtIndexPath(indexPathForCell) != nil) {
cellWithCategories["Recomendation"]?.removeLast()
tableView.beginUpdates()
tableView.deleteRowsAtIndexPaths([indexPathForCell], withRowAnimation: .None)
tableView.endUpdates()
}
checkMark.hide()
checkMark.userInteractionEnabled = true
}
}
}
code for number of rows :
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let sectionKey = keysForSectionTableView[section]
let numberOfRows = cellWithCategories[sectionKey]
return (numberOfRows?.count)!
}
I don't see any code that will make your table view scroll to top.
But you can try change animation to none. If doesn't work then there is must be some other code, thats causing this issue.
let indexPathForCell = NSIndexPath(forRow: 5, inSection: 1)
tableView.beginUpdates()
tableView.insertRowsAtIndexPaths([indexPathForCell], withRowAnimation: .None)
tableView.endUpdates()

UITableView add cell Animation

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)
}

Resources