Swift 3
I have a table view with several rows, each row has a button (uses indexPath.row), when the button is clicked it moves the row from Section 1 (testArray) to Section 0 (followedArray). BUT when using the search bar to search through testArray and a row's button is clicked it crashes.
Now when using the search bar, testArray is filtered into a new array called filteredArray. The button is supposed to move the row from filteredArray to followedArray.
Without searching, the button works as intended, it only crashes when using the search bar.
What am I doing wrong?
Here is the ERROR I get:
CustomCellSwift[1473:391841] *** Assertion failure in -[UITableView _endCellAnimationsWithContext:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKit/UIKit-3600.5.2/UITableView.m:1315
New Suggestion: Could it have anything to do with it having a conflict with the indexPath.row of testArray and followedArray when filteredArray replaces the table view?
Deleting from UISearchController's filtered search results
The code I'm using:
#IBAction func followButtonClick(_ sender: UIButton!) {
// Adding row to tag
let buttonPosition = (sender as AnyObject).convert(CGPoint.zero, to: self.myTableView)
if let indexPath = self.myTableView.indexPathForRow(at: buttonPosition) {
// Change Follow to Following
(sender as UIButton).setImage(UIImage(named: "follow.png")!, for: .normal)
cell.followButton.isHidden = true
cell.followedButton.isHidden = false
// Checking wether to import from testArray or filteredArray to followedArray
if !(searchController.isActive && searchController.searchBar.text != "") {
self.myTableView.beginUpdates()
// ----- Inserting Cell to followedArray -----
followedArray.insert(testArray[indexPath.row], at: 0)
myTableView.insertRows(at: [IndexPath(row: 0, section: 0)], with: .fade)
// ----- Removing Cell from testArray -----
testArray.remove(at: indexPath.row)
let rowToRemove = indexPath.row
self.myTableView.deleteRows(at: [IndexPath(row: rowToRemove, section: 1)], with: .fade)
self.myTableView.endUpdates()
}
else {
self.myTableView.beginUpdates()
// ----- Inserting Cell to followedArray -----
followedArray.insert(filteredArray[indexPath.row], at: 0)
myTableView.insertRows(at: [IndexPath(row: 0, section: 0)], with: .fade)
// ----- Removing Cell from filteredArray -----
filteredArray.remove(at: indexPath.row)
let rowToRemove = indexPath.row
self.myTableView.deleteRows(at: [IndexPath(row: rowToRemove, section: 0)], with: .fade)
self.myTableView.endUpdates()
}
}
}
So I found the answer, I was adding the object to the array using insert at sections method instead of using just insert since I only have 1 section.
let testObject: Test = filteredArray[indexPath.row]
let indexOfObjectInArray = testArray.index(of: testObject)
followedArray.insert(testObject, at: 0)
Related
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 items in a tableview , and i want to reload or update the tableview data , except the first row or the first indexpath of the tableview.
let visibleIndex = self.tableView.visibleCells.compactMap {
tableView.indexPath(for: $0) }
self.tableView.reloadRows(at: visibleIndex, with: .automatic)
However this reloads all the visible cells , how do i reload all the visible cells except the first row
No need to get the visible cells. Use indexPathsForVisibleRows and remove the index path for section 0, row 0.
let allButFirst = (self.tableView.indexPathsForVisibleRows ?? []).filter { $0.section != 0 || $0.row != 0 }
self.tableView.reloadRows(at: allButFirst, with: .automatic)
Assuming the first visible cell is the 1 on the most top You can do
self.tableView.reloadRows(at: Array(visibleIndex.dropFirst()), with: .automatic)
Or if not
let visibleIndex = self.tableView.visibleCells.compactMap {
tableView.indexPath(for: $0) }.filter { $0.row != 0 }
self.tableView.reloadRows(at: visibleIndex, with: .automatic)
==
let visibleIndex:[IndexPath] = self.tableView.visibleCells.compactMap { item in
let index = self.tableView.indexPath(for:item)!
return index.row != 0 ? index : nil
}
I'm using Cristik's answer from this post: Drop-Down List in UITableView in iOS
The code works fine, but I am trying to create logic so when the user clicks on a closed cell, every other open cell closes. So if the Account cell was open, when the user clicks on the Event one, the event one opens and the Account one closes.
I tried the following logic:
for (var x = 0; x < displayedRows.count; x++) {
let view = displayedRows[x]
if (view.isCollapsed == false && x != indexPath.row){
let range = x+1...x+view.children.count
displayedRows.removeRange(range)
let indexPaths = range.map{return NSIndexPath(forRow: $0, inSection: indexPath.section)}
tableView.deleteRowsAtIndexPaths(indexPaths, withRowAnimation: .Automatic)
}
}
Here is the full function:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: false)
let viewModel = displayedRows[indexPath.row]
if viewModel.children.count > 0 {
let range = indexPath.row+1...indexPath.row+viewModel.children.count
let indexPaths = range.map{return NSIndexPath(forRow: $0, inSection: indexPath.section)}
tableView.beginUpdates()
if viewModel.isCollapsed {
displayedRows.insertContentsOf(viewModel.children, at: indexPath.row+1)
tableView.insertRowsAtIndexPaths(indexPaths, withRowAnimation: .Automatic)
for (var x = 0; x < displayedRows.count; x++) {
let view = displayedRows[x]
if (view.isCollapsed == false && x != indexPath.row){
let range = x+1...x+view.children.count
displayedRows.removeRange(range)
let indexPaths = range.map{return NSIndexPath(forRow: $0, inSection: indexPath.section)}
tableView.deleteRowsAtIndexPaths(indexPaths, withRowAnimation: .Automatic)
}
}
}
else {
displayedRows.removeRange(range)
tableView.deleteRowsAtIndexPaths(indexPaths, withRowAnimation: .Automatic)
}
tableView.endUpdates()
}
viewModel.isCollapsed = !viewModel.isCollapsed
lastIndex = indexPath.row
}
I've tried variations of this code as well but for some reason keep getting the following error: Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'attempt to insert row 6 into section 0, but there are only 6 rows in section 0 after the update'
I know that this is a datasource problem but I can't seem to find a solution. Any solution/or alternative way to accomplish this would be appreciated! Thanks
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()