Animate NSView’s scrollToVisible

When I ported my game Empire for macOS (see: https://apps.apple.com/de/app/empire/id1287139467?mt=12) to iOS (see: https://apps.apple.com/de/app/empire-mobile/id1465452819) I noticed a much nicer scrolling behaviour there. There are a lot of smaller and more fundamental differences in programming for these two operating systems, especially between AppKit and UIKit and animation is one of them.

So, in iOS I used the UIView method

to scroll my View (a map) embedded in a ScrollView to a specified rectangle around the blinking cursor. I set animated to true and the scrollView scrolls smooth and nice to the desired region. Fine.

In macOS there is a similar method for NSView:

Looks quite the same besides the additional parameter “animated” in the iOS version. And that’s the point of this post. I want to have a similar function with scroll animation in macOS as well.

Looking around a little bit I found this discussion in stackoverflow which sounds quite promising. Well, I want to scroll to a rect not to a point but that’s not too difficult to adjust:

Please note, that I use flipped coordinates in my NSView to make it match the iOS behaviour. So, this new method does the following

  • If the view is really embedded in a scroll view I make a copy of the origin of the clipview’s bounds. This should be the origin of the currently visible rect in view coordinates.
  • Then I shift this origin in x and y coordinates separately with the shortest possible movement to make the supplied rect completely visible (if it’s not too big).
  • And finally I set this new bounds with animation as proposed in the mentioned stackoverflow post.

That’s fine but it turns out that there is a problem with bounds.origin of the clipview. If the view is getting resized (e.g. by resizing the surrounding window) bounds.origin is somehow shifted against the true origin of the visible rectangle in y-direction. I could not figure out why and by how much. Well, there is also this statement in the Apple docs not to manipulate the clipview directly since its main purpose is to function internally as a scrolling machine for views.

But I do know the true origin of the visible area. It’s part of the clipview’s documentVisibleRect. So I take that origin for the calculation of the scrolled origin of the visibleRect and shift the bounds.origin of the clipview by the same amount, and voilà: that works even if the view is getting resized.

Here is my final implementation of the new method of my NSView:

BTW: the animation duration in the iOS version scrollRectToVisible is 0.3 seconds.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.