iosuitableviewcontentoffset

Observe current contentOffset during UiTableView's delete rows animation


I set up UI behavior based on tableView's content offset. It works fine and smoothly in all cases excepting one: when tableView starts deleting rows with animation.

The problem appears when before deleting there was a lot of content and a user scrolled a part of it, but after deleting tableView can show the whole content without scrolling. In that case table view changes content offset smoothly and simultaneously with deleting animation.

The main reason causing my problem is scrollView delegate's method scrollViewDidScroll is not invoked during deleting animation and my behavior doesn't work.

I tried to use KVO for observing offset changes:

observation = tableView.observe(\.contentOffset, options: [.new, .old]) { (_, change) in
    print(change)
}

It works better, but fires only once at the first moment with final value so I don't receive any intermediate values which I need for making smooth UI reaction.

Ok, I tried to animate my behavior using UIView.animate and siblings. But found it's difficult to choose parameters to synchronize my animations and tableview's offset animation.

So, what I missed? Is it possible to observe any intermediate offset values during rows deletion animation?


Solution

  • Ok, guy! I have a good piece of news for you =] You can use Layer Presentation Tree to observe UI values each moment.

    1. Wrap tableView updates into CATransaction
        let displayLink = CADisplayLink(target: self, selector: #selector(adjustHeader))
        displayLink.add(to: .current, forMode: .common)
    
        CATransaction.begin()
        CATransaction.setCompletionBlock(displayLink.invalidate)
        // do tableView updates here
        CATransaction.commit()
    
    
    1. Use layer presentation tree to calculate current offset. For example:
    @objc private func adjustHeader() {
        guard
            let headerPresentationLayer = headerView.layer.presentation(),
            let viewPresentationLayer = view.layer.presentation(),
            let tableViewPresentationLayer = tableView.layer.presentation()
            else { return }
    
        let origin = tableViewPresentationLayer.convert(headerPresentationLayer.frame.origin, to: viewPresentationLayer)
        print("LayerPresentationTree: \(origin)")
        offsetDepandableView.frame.origin = origin
    }