I'm working with UITableView and I want to add new items once at a time instead of adding them all at one time when there are multiple new items. Right now, my code is like this:
for rowNum in 0...(counter-1) {
let when = DispatchTime.now() + 1
DispatchQueue.main.asyncAfter(deadline: when) {
self.tableView.insertRows(at: [IndexPath(row: rowNum, section: lastSection)], with: .fade)
}
}
But it's not working.
It's been a while but maybe I can help. While inserting new rows or sections, you need to use tableView.beginUpdates() and tableView.endUpdates(). Also, between these two functions, row numbers and the count of the array should be equal. Otherwise an error is thrown. Try this:
let when = DispatchTime.now() + 1
DispatchQueue.main.asyncAfter(deadline: when) {
self.tableView.beginUpdates()
self.<yourArray>.append(<newElement>)
self.tableView.insertRows(at: [IndexPath(row: self.<yourArray>.count - 1,
section: lastSection)], with: .fade)
self.tableView.endUpdates()
}
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
)
}
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()
}
I have a question about making a table view section expand/collapse. There are many pieces of information on the web about this topic, but my question is a little bit different.
My question is, how can I make an additional section when the user taps on another section?
The following images may help you understand my situation.
At the top of the table view we have one small section (section 0).
If I tap it...
Section 1 and 2 should be created between section 0 and 3.
How can I do this? Is there any way or code? Please help me.
Also, I tried this code but got a sigabrt error :(
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.tableView.beginUpdates()
let indexSet = NSMutableIndexSet()
indexSet.add(indexPath.section + 1)
indexSet.add(indexPath.section + 2)
if indexPath.section == 0 {
if expandCol == true {
self.tableView.deleteSections(NSIndexSet(index: 1) as IndexSet, with: .automatic)
self.tableView.deleteSections(NSIndexSet(index: 2) as IndexSet, with: .automatic)
expandCol = !expandCol
} else {
self.tableView.insertSections(NSIndexSet(index: 1) as IndexSet, with: .automatic)
self.tableView.insertSections(NSIndexSet(index: 2) as IndexSet, with: .automatic)
expandCol = !expandCol
}
}
self.tableView.endUpdates()
self.tableView.reloadData()
}
You can keep a flag variable on view-controller for selectedSection.
var selectedSection: Int = -1
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return section == selectedSection ? <section-row-count> : 0
}
Set the flag value in section header action method:
func actionSectionHeader(tag: Int) {
selectedSection = tag
self.tableView.reloadData()
}
To add a new section into UITableView, you need
1 - Update your data. For example, insert your sections in a data array, or change their visibility flags.
2 - Call the following method.
tableView.insertSections([1, 2], with: .automatic)
or
tableView.reloadData()
The first method will update your UITableView with an animation. The second one will update it instantly. For better UX I'd recommend you to use the first one.
Update on your code
At first, you can pass all sections indices at once using a simple array. Like that
if expandCol {
self.tableView.deleteSections([1, 2], with: .automatic)
} else {
self.tableView.insertSections([1, 2], with: .automatic)
}
expandCol = !expandCol
The second problem is that you're calling reloadData after you call the animated update of your table.
And the most important, you need to change the data. You UITableView has a dataSource and it returns a number of sections and cells. After user interacts with your trigger cell, you need to make changes at that data before calling the update.
I am trying to like a post in a tableview, after getting response i need to update like status in my tableview without knowing to user that table is reloading.
let indexPath = IndexPath(item: tag, section: 0)
self.tableCommunity.reloadRows(at: [indexPath], with: .fade)
buddy try using code :-
let indexPath = IndexPath(item: tag, section: 0)
self.tableCommunity.reloadRows(at: [indexPath], with: .none)
Hope it will work.