I have a UIViewController
that holds a NSFetchedResultsController
. After the insertion of rows to the top, I want to keep the rows visible as they where before the insertions. This means I need to make some calculations to keep the contentOffSetY right after the update. The calculation is correct, but I noticed that scrollViewDidScroll
gets called after it scrolled to my specified contentOffsetY, this results in a corrupted state. This is the logging:
Will apply an corrected Y value of: 207.27359771728516
Scrolled to: 207.5
Corrected to: 207.27359771728516
Scrolled to: 79.5 <-- Why is this logline here?
You can directly clone an example project: https://github.com/Jasperav/FetchResultControllerGlitch (commit https://github.com/Jasperav/FetchResultControllerGlitch/commit/d46054040139afeeb648e1e0b5b113bd98685b4a, the newest version of the project only glitches, the weird call to the scrollViewDidScroll method is now gone. If you fix the glitch I award the bounty. Just clone the newest version, run it and scroll a little bit. You will see strange content offset's (glitches)). Run the project and you will see the strange output. This is the controllerDidChangeContent
method:
public func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
let currentSize = tableView.contentSize.height
UIView.performWithoutAnimation {
tableView.endUpdates()
let newSize = tableView.contentSize.height
let correctedY = tableView.contentOffset.y + newSize - currentSize
print("Will apply an corrected Y value of: \(correctedY)")
tableView.setContentOffset(CGPoint(x: 0,
y: correctedY),
animated: false)
print("Corrected to: \(correctedY)")
}
}
If I call tableView.layoutIfNeeded
right after the tableView.endUpdates()
, the delegate is already called. What does it cause to call the delegate method? Is there any way it does not scroll?
I downloaded your code and made some tweaks to fix the glitching issue. Here is what I have done.
estimatedRowHeight
property of table view to some number init() {
super.init(frame: .zero, style: .plain)
estimatedRowHeight = 50.0
register(MyTableViewCell.self, forCellReuseIdentifier: "cell")
}
func reloadDataWithoutScroll() {
let lastScrollOffset = contentOffset
beginUpdates()
reloadData()
layoutIfNeeded()
endUpdates()
layer.removeAllAnimations()
setContentOffset(lastScrollOffset, animated: false)
}
controllerDidChangeContent
function to make use of the reloadDataWithoutScroll
function UIView.performWithoutAnimation {
tableView.performBatchUpdates({
tableView.insertRows(at: inserts, with: .automatic)
})
inserts.removeAll()
tableView.reloadDataWithoutScroll()
}
When I execute with these changes, it doesn't scroll when a new row is added. However, it does scroll to show the new added row when the current contentOffset is 0. And I don't think that would be a problem, logically speaking.