How to animate the footer view a table view when the UItableView reloads?
I am trying to append an element to the tableView and when the table view reloads the footer view should come with some animation. And also if the table view contents exceeds the screen size the footer view should stick with bottom of the screen. Here is my try:
tableView.beginUpdates()
self.familyAModelArray.append(self.childAModelArray)
tableView.endUpdates()
Note: Here i want to append an object to an array which is the content of the UITableView and reload the TableView. Thanks in advance!
To animate the insert action, you need to call insertRowsAtIndexPaths method with row animation.
After adding table cell elements to your array, you must calculate the index paths for the new cells to be displayed, and create an array of them, like this
let indexes = [IndexPath(row: 0, section: 0), IndexPath(row: 1, section: 0)]
Then simply call the following method to animate the insertion
tableView.beginUpdates()
tableView.insertRowsAtIndexPaths(indexes, withRowAnimation: .Fade)
tableView.endUpdates()
If you want the footer view to stick on the bottom of your screen, then it shouldn't be a footer view. it should be a custom UIView that is pinned to your view controller's view. Whenever you call a reloadData() on your tableView, you can likewise call a present/animate function on your new custom view that you pinned to the bottom & animate it on / off the screen.
I have solved this by using animation on UIView when the UITableView reloads after appending the rows.
UIView.transition(with: tableView, duration: 0.2, options: .curveEaseIn, animations: {self.tableView.reloadData()}, completion:{ (success) in
if success {
if self.familyAModelArray.count >= 3{
self.scrollToBottom()
}
}
})
And if length of my array is greater than 3 the footer view stick at the bottom by scrolling the UITableView to the top .
func scrollToBottom(){
DispatchQueue.global(qos: .background).async {
let indexPath = IndexPath(row: self.familyAModelArray.count-1, section: 0)
self.tableView.scrollToRow(at: indexPath, at: .top, animated: true)
}
}
This solution has given a nice look for my table view.
Related
I've built a pagination mechanism for my table view so when the user scrolls to the last row in section the app loads additional content if possible. When additional content gets loaded, I perform a batch update on my table view like this:
guard
let sectionIndex = self.sections.firstIndex(of: .itemsSection),
!loadedItems.isEmpty
else { break }
let previousNumberOfRows = self.tableView.numberOfRows(inSection: sectionIndex)
let additionalIndexPaths = self.generateIndexPaths(
startIndex: previousNumberOfRows,
count: loadedItems.count,
section: sectionIndex
)
self.tableView.performBatchUpdates({
self.items.append(contentsOf: loadedItems)
self.tableView.insertRows(at: additionalIndexPaths, with: .automatic)
}, completion: nil)
and here's a function that generates index paths to insert new rows at:
private func generateIndexPaths(startIndex: Int, count: Int, section: Int) -> [IndexPath] {
var indexPaths: [IndexPath] = []
for row in startIndex..<startIndex + count {
indexPaths.append(IndexPath(row: row, section: section))
}
return indexPaths
}
It works perfectly fine until the update happens while table view is scrolling. Additional content is getting loaded so fast that the scroll animation have no time to finish. It starts to jump and additional cells pop with a broken animation. Since my table view uses UITableView.automaticDimension, I thought it was because of the wrong estimated height for newly created cells, so I implemented tableView(_:estimatedHeightForRowAt:) -> CGFloat method and gave those cells a pretty accurate height value. But it didn't help fixing the jumps. After trying all possible UITableView.RowAnimation fitting my need, I decided to completely disable the update animation. Here's how I'm currently updating the table view:
self.items.append(contentsOf: loadedItems)
UIView.setAnimationsEnabled(false)
self.tableView.insertRows(at: additionalIndexPaths, with: .none)
UIView.setAnimationsEnabled(true)
This approach gets rid of the update animation and at the same time introduces another problem: it feels like the table view is unresponsive during the update animation, like if I set isUserInteractionEnabled = false for a second. So what is the best way to update the table view with new rows with no animation and jumps? Thanks in advance :)
I have some problem with UITableView scrolling. This is my simple screen where I have UITextField and UITableView with cells. When user type something in UITextField app filters list of items and reloads UITableView. And it is working as expected.
On the right screen you can see how UITableView looks when I scroll. Cells go under UITextField.
Let's assume I scrolled some cell and If I start to type something in UITextField I got list of filtered cells but some of them are under UITextField and I can't scroll them down.
By default the table's scroll offset doesn't change when you reload the table with new set of row data. You need to scroll back to top after reloading the table :
self.tableView.scrollToRow(at: IndexPath(row: 0, section: 0),
at: .top,
animated: true)
and if you don't have a default place-holder cell to represent "No Matching Results" when there are no results and thus no cells to represent at Index (0,0) then add following condition before the above code to safeguard a crash:
guard cellResults.count > 0 else { return }
First of all your UITextField contained on tableview header view? UITableView need scrolling with table or not?
If your need always show first cell when user input something in UITextField, i recommend it with RxSwift like this
textField
.rx.text
.orEmpty
.debounce(0.1, scheduler: MainScheduler.instance)
.distinctUntilChanged()
.subscribe(onNext: { [unowned self] query in
//TODO: Do something with you table, for example
// self.tableView.scrollToRow(at: IndexPath(row: 0, section: 0), at: .top, animated: true)
// or update tableView.
})
.disposed(by: disposeBag)
I have collection views (in plural) inside a table view of many sections. Just so we're clear, a single table view with many sections with only one row each being that row an individual collection view.
All set up is working just fine, the data is well divided and delegates are all wired up recognizing everything they need to recognize. My problem is kind of simple but difficult at the same time: I want to scroll to specific collection view's position whenever I need to find a specific cell in animated fashion.
So far I'm able to jump with no problem to both table section (indexPath.section) and collection item (indexPath.row). The issue arises when I need to scroll (simultaneously) with animation.
My findings so far
I'm only able to achieve my current goal deactivating scroll animations for UITableView (UICollectionView can perform well with/out it)
Whenever I set UITableView selectRow or scrollToRow animation flags to true then the app crashes (99% sure this happens because I'm trying to access and "invisible" section due to the animation hasn't shown it yet).
Relevant snippets of code
#IBOutlet weak var albumTableView: UITableView!
#IBOutlet weak var stickersCollectionView: UICollectionView!
func locateCell() {
...
let stickerIndex = methodThatReturnsExactIndex()
let sectionIndex = IndexPath(row: 0, section: stickerIndex.section)
albumTableView.selectRow(at: sectionIndex, animated: false, scrollPosition: .top)
let rowIndex = IndexPath(item: stickerIndex.row, section: 0)
stickersCollectionView.scrollToItem(at: rowIndex, at: 0, animated: true)
}
I was thinking in experiment with the UIScrollViewDelegate (detecting when the tableview and the collectionview stopped in order to perform the scrolling) but that would imply spreading global variables around the code and experience tough me that's just racing conditions waiting to happen. Any help will be appreciated.
First Scroll your tableView to that specific index with/without animation. This will make that cell visible now get your cell by providing that indexPath so you could access the collectionView object inside your tableViewCell. Then ask you collectionView to scroll to specific indexPath with/without animation.
Take another global bool to store that tableView is begin scrolling. Also store both indexPath used for collection and tableView and use tab
tableViewIsScrolling = true
let yourSelectedIndexPathForTableView = IndexPath(row: 0, section: 4)//store it globally
let yourSelectedIndexPathForCollectionView = IndexPath(row: 10, section: 0)//store it globally
tableView.scrollToRow(at: yourSelectedIndexPathForTableView, at: .middle, animated: false)
func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
if tableViewIsScrolling {
tableViewIsScrolling = false
//Perform your scrolling of CollectionView
guard let yourCell = tableView.cellForRow(at: yourSelectedIndexPathForTableView) as? YourCell else {return}
yourCell.collectionView.scrollToItem(at: yourSelectedIndexPathForCollectionView, at: .centeredHorizontally, animated: true)
}
}
I have a tableview (style - grouped) which contains more rows than can fit into its frame so you have to scroll to see the last row. When I delete the last row the tableview scrolls its content down so the new last row is at the bottom of the frame (kind of fill the empty space).
How can I prevent a UITableView from scrolling after I delete the last row in it? I want it to keep the empty space and do not scroll automatically.
I have tried to set content insets but it doesn't seem to prevent scrolling unfortunately.
EDIT: My code for removing the row:
func removeLastRow() {
let indexPath = IndexPath.init(row: self.messages.count - 1, section: 0)
self.messages.removeLast()
CATransaction.begin()
self.tableView.beginUpdates()
CATransaction.setCompletionBlock { () -> Void in
// do stuff, add new rows
}
self.tableView.deleteRows(at: [indexPath], with: .left)
self.tableView.endUpdates()
CATransaction.commit()
}
Retreive the cells size before deleting by calling tableView:heightForRowAt: and after deleting, set the vertical content offset of your table view manually to the current vertical content offset, plus the height of the cell.
let deletedCellHeight = tableView.heightForRowAt(indexPath)
Then:
tableView.contentOffset.x += deletedCellHeight
using this code to scroll to bottom of collection view. however it scrolls to the second last one only.
private func scrollToBottom() {
let lastSectionIndex = (ChatCollectionView?.numberOfSections())! - 1
let lastItemIndex = (ChatCollectionView?.numberOfItemsInSection(lastSectionIndex))!-1
let indexPath = NSIndexPath(forItem: lastItemIndex, inSection: lastSectionIndex)
ChatCollectionView!.scrollToItemAtIndexPath(indexPath, atScrollPosition: UICollectionViewScrollPosition.Bottom, animated: false)
}
is anyone familiar with a bug like that
Your collection view's frame was under the visible area. You should set its bottom inside visible area.