2

I use the second right bar button item as an indicator when there is a successful CloudKit sync. However, if the tableView is held in scroll (with items now under the navigation bar) when the indicator appears, the tableView bounces in sync with the animation. This does not happen if the user is not interacting with the tableView.

Here is a GIF demonstrating the effect.

Animated screenshot

The other UIBarButtonItems are set up in the storyboard. The one for my iCloud sync indicator is set up in code in viewDidLoad():

var cloudIndicator = UIImageView()
cloudIndicator.frame = CGRect(x: 0, y: 0, width: 25, height: 25)
cloudIndicator.contentMode = .center
cloudIndicator.transform = CGAffineTransform.identity
// Get existing right bar button item which was set up in storyboard
var rightButtonItems = self.navigationItem.rightBarButtonItems ?? []
let customButtonItem = UIBarButtonItem(customView: cloudIndicator)
rightButtonItems.append(customButtonItem)
self.navigationItem.rightBarButtonItems = rightButtonItems

This is the method that animates the cloud sync indicator:

func cloudLabelImageAlert(_ image: UIImage, tintColor: UIColor = .darkGray) {
    DispatchQueue.main.async {
        self.cloudIndicator.alpha = 0
        self.cloudIndicator.transform = CGAffineTransform(scaleX: 0.1, y: 0.1)
        self.cloudIndicator.tintColor = tintColor
        self.cloudIndicator.image = image
        // Animate icon appearing
        UIView.animate(withDuration: 0.4, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 10, options: [], animations: {
            self.cloudIndicator.alpha = 1
            self.cloudIndicator.transform = CGAffineTransform.identity
        }, completion: { didFinish in
            // Animate icon disappearing
            UIView.animate(withDuration: 0.4, delay: 2.0, usingSpringWithDamping: 1.0, initialSpringVelocity: 0, options: [], animations: {
                self.cloudIndicator.alpha = 0
                self.cloudIndicator.transform = CGAffineTransform(scaleX: 0.1, y: 0.1)
            }, completion: nil)
        })
    }
)

Presumably this problem relates to the frame of the image view changing during the animation, but it seems strange that it only happens while the tableView is being interacted with.

Is there a way to prevent this happening, or a better way to animate an image view as a bar button item?


Edit

Thanks to advice in the comments, it turns out this is due to reloading the tableview and nothing to do with the animation.

I found the problematic code, which is called after a CloudKit sync:

if let index = self.tableView.indexPathForSelectedRow {
    self.tableView.deselectRow(at: index, animated: true)
    DispatchQueue.main.asyncAfter(deadline: .now()+1) {
        self.tableView.reloadData() // This is delayed as it was causing problems with autoselection (e.g. after coming from Spotlight or notification)
        let image = UIImage(named: "cloudTick")!
        self.cloudLabelImageAlert(image, tintColor: self.colors[0])
    }
} else {
    self.tableView.reloadData()
    let image = UIImage(named: "cloudTick")!
    self.cloudLabelImageAlert(image, tintColor: self.colors[0])
}

Commenting out the self.tableview.reloadData() lines stopped the glitch but the animation continued as expected.

I need to update the data at this point for the user. Is there a better way to do this?

Chris
  • 4,009
  • 3
  • 21
  • 52
  • 1
    Because he tableView is reloading its cells when syncing. – ielyamani Sep 21 '18 at 14:17
  • @Carpsen90 Thanks. I will check that, but this is only triggered after a successful sync (not at a reload, though I will check this), and the glitch seems to be in time with the animation for the duration of the animation. Surely a reload would just be a single point in time? – Chris Sep 21 '18 at 18:14
  • Could you share code where you use `cloudLabelImageAlert`? Would be helpful. – Pranav Kasetti Sep 24 '18 at 13:25
  • @PranavKasetti I will share this later once I’m back at my computer. Thanks – Chris Sep 24 '18 at 13:26
  • @Chris Are you sure your not invoking `cloudLabelImageAlert(image:, tintColor:)` method while loading new table cells ? – Sharkes Monken Sep 25 '18 at 15:28
  • I have edited the question. Carpsen90, Pranav and Sharkes were right - I was reloading the tableview data and this was the actual cause of the problem. I would be happy to accept an answer and award the bounty accordingly. – Chris Sep 26 '18 at 05:30
  • Also, [this question](https://stackoverflow.com/q/8640409/8289095) seems to provide some workarounds for this. – Chris Sep 26 '18 at 08:27
  • @Carpsen90 Question updated – Chris Sep 26 '18 at 17:58
  • @PranavKasetti Question updated – Chris Sep 26 '18 at 17:59
  • @SharkesMonken Question updated – Chris Sep 26 '18 at 17:59

3 Answers3

2

It seems for some mysterious reason your navigation bar is showing "large title" for a moment which leads to contentInset change of the tableView.
So try to manually disable large titles at all in viewDidLoad:

if #available(iOS 11.0, *) {
     navigationItem.largeTitleDisplayMode = .never
     navigationController?.navigationBar.prefersLargeTitles = false
}
arturdev
  • 10,884
  • 2
  • 39
  • 67
  • Hi. Thanks for your answer. The large title is deliberate. The first movement in the gif is me scrolling the tableView manually. The jerky movement after that is the problem. – Chris Sep 26 '18 at 20:22
1

As mentioned on the comment section, you where probably invoking cloudLabelImageAlert(image:, tintColor:) method while loading new table cells.

About updating the data, i would suggest invoking table view reloadData() method after the cloud animation completes.

Sharkes Monken
  • 661
  • 5
  • 23
  • Thanks for this. You were absolutely right about it being the reloading of the tableView causing the glitch and not the animation at all! – Chris Sep 27 '18 at 19:07
1

Hi i had somwhat similar glitch not same but for me it got fixed by putting self.automaticallyScrollInsets to false in viewDidLoad and while animating the imageView for hide and show in the final UIView.animate completion block i called reloadData(). Hope this helps and thanks !

Ashish
  • 204
  • 1
  • 8