While I delete a row from tableview it renders content in a strange way with empty space below and above - see video record and bug image:
deformed table view after call deletion
Probably, it may be caused by constraints changes (top view expanding / collapsing), or incorrect delete operations.
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let contentOffSetY = scrollView.contentOffset.y
switch true {
case contentOffSetY < 0 && searchViewIsHidden && !topMainViewIsHidden:
changeSearchVisibility(false)
case contentOffSetY > 0 && !searchViewIsHidden && !topMainViewIsHidden:
changeSearchVisibility(true)
case contentOffSetY > maxHeaderHeight && searchViewIsHidden && !topMainViewIsHidden:
transformTopView(toBig: false)
case self.contentOffSetY <= 0 && searchViewIsHidden && topMainViewIsHidden:
transformTopView(toBig: true)
default:
break
}
}
// Indexes of items that should be changed from Realm Observer
func updateTableView(_ deletions: [Int],
_ insertions: [Int], _ modifications: [Int]) {
tableView.beginUpdates()
tableView.insertRows(at: insertions.map({ IndexPath(row: $0, section: 0) }),
with: .none)
tableView.deleteRows(at: deletions.map({ IndexPath(row: $0, section: 0)}),
with: .none)
tableView.reloadRows(at: modifications.map({ IndexPath(row: $0, section: 0) }),
with: .none)
tableView.endUpdates()
}
Maybe not a perfect solution but you could try redrawing the entire tableview after deleting a row. With reloadData()
Let me know if this works for you.
Related
I am currently working on a app that shows nearby annotations to user by TableView. Cells are ordered by distance to user's location. I want to animate the cells when they change order due to distance. How can I do that ?
Here's my view
I had tried this code but it didn't work.
tableView.beginUpdates()
tableView.moveRow(at: IndexPath(row: fromIndex, section: 0), to: IndexPath(row: toIndex, section: 0))
tableView.endUpdates()
Edit : I tried like this one it didn't work like the way I want, but it was really close. The problem is, all the cells are animating when a single cell changes it's order
didUpdateLocations :
sortedAnnotationArrayWithModel = annotationArrayWithModel.sorted { $0.distance < $1.distance }
tableView.reloadData()
tableView.beginUpdates()
for i in 0..<sortedAnnotationArrayWithModel.count {
let oldIndex = annotationArrayWithModel.firstIndex(of: sortedAnnotationArrayWithModel[i])
let newIndex = i
if oldIndex != newIndex {
tableView.moveRow(at: IndexPath(row: oldIndex!, section: 0), to: IndexPath(row: newIndex, section: 0))
}
}
tableView.endUpdates()
Try wrapping your changes in a call to performBatchUpdates(_:completion:)
You pass that function a closure that does the reordering, and it animates the changes.
I was wondering if it is posible to reload a tableView without reloading the first cell/row in a simple way? If so, how would you do it?
let itemArray:[Int] = []
for i in 1..<itemArray.count {
let indexPath = IndexPath(item: i, section: 0)
tableView.reloadRows(at: [indexPath], with: .fade)
}
If your table is not grouped currently ,
The best way to do this is add your 1st cell to one section And all other cells to another section. Then you can easily reload all cell excluding 1st cell using
//section 0 - your 1st cell
//section 1 - other cells
tableView.reloadSections([1], with: .none)
Just use the reloadRows method to reload your tableView.
var dataSource: [SomeData] = []
func reloadAllButFirstRow() {
guard dataSource.count > 0 else { return }
tableView.reloadRows(
at: (1...dataSource.count).map { IndexPath(row: $0, section: 0) },
with: .fade
)
}
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()
I have the request to realm database
func allContacts() -> Results<RMContact> {
let realm = self.encryptedRealm()
return realm!.objects(RMContact.self).sorted(byKeyPath: "lastActive", ascending: false)
}
And code from presenter
DispatchQueue.main.async {
let contacts = RMContactsManager.shared.allContacts()
self.notificationToken = contacts.observe { [weak self] (changes: RealmCollectionChange) in
guard let tableView = self?.view.tableView else { return }
switch changes {
case .initial:
UIView.performWithoutAnimation {
tableView.reloadData()
}
case .update(_, let deletions, let insertions, let modifications):
UIView.performWithoutAnimation {
tableView.beginUpdates()
tableView.insertRows(at: insertions.map({ IndexPath(row: $0, section: 0) }), with: .automatic)
tableView.deleteRows(at: deletions.map({ IndexPath(row: $0, section: 0)}), with: .automatic)
tableView.reloadRows(at: modifications.map({ IndexPath(row: $0, section: 0) }), with: .automatic)
tableView.endUpdates()
}
case .error(let error):
fatalError("\(error)")
}
}
}
After setting new lastActive value sequence in tableview didn't change
For the first time sorting is actual for the controller, but after setting new values to lastActive property no changes. Is it an observer problem?
The issue seems to be that you are getting changes in terms of insertions, deletions and modifications.
If you will first do the insertion and then deletion, (This is what you are doing right now), It will insert new values and the datasource will be modified so now when you delete the rows, it will not be correct.
Also in your case, where you are sorting the array, generally two elements will be replaced, one being deleted and other will be added.
So what can solve your problem is, first do deletions and then do insertions. Like this:
UIView.performWithoutAnimation {
tableView.beginUpdates()
tableView.deleteRows(at: deletions.map({ IndexPath(row: $0, section: 0)}), with: .automatic)
tableView.insertRows(at: insertions.map({ IndexPath(row: $0, section: 0) }), with: .automatic)
tableView.reloadRows(at: modifications.map({ IndexPath(row: $0, section: 0) }), with: .automatic)
tableView.endUpdates()
}
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()