Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

scrollViewDidScroll called with strange contentOffsetY

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?

like image 909
J. Doe Avatar asked Nov 20 '25 11:11

J. Doe


1 Answers

I downloaded your code and made some tweaks to fix the glitching issue. Here is what I have done.

  1. set estimatedRowHeight property of table view to some number
    init() {
        super.init(frame: .zero, style: .plain)
        estimatedRowHeight = 50.0
        register(MyTableViewCell.self, forCellReuseIdentifier: "cell")
    }
  1. created a function to handle the UITableView reload action without modifying the current contentOffset
    func reloadDataWithoutScroll() {
        let lastScrollOffset = contentOffset
        beginUpdates()
        reloadData()
        layoutIfNeeded()
        endUpdates()
        layer.removeAllAnimations()
        setContentOffset(lastScrollOffset, animated: false)
    }
  1. Updated 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.

like image 142
Senthil_Kumar Avatar answered Nov 23 '25 04:11

Senthil_Kumar