I know this is a little off topic, but I wanted to have a similar method for scrolling to a rectangular box with animation, as in UIView scrollRectToVisible(_ rect: CGRect, animated: Bool) for my NSView . I was happy to find this post, but obviously the accepted answer does not always work correctly. It turns out that there is a problem with bounds.origin clip. If the size of the view changes (for example, by resizing the surrounding window), bounds.origin somehow shifts relative to the true beginning of the visible right-angled box in the y direction. I could not understand why and by how much. Well, there is also an expression in Apple docs that it should not directly manipulate the viewing of a clip, since its main purpose is to function as a scroll to view.
But I know the true origin of the visible region. This is part of documentVisibleRect clips. Therefore, I take this source to calculate the scrollable visibleRect source and shift the bounds.origin clip view by the same amount, and voila: this works even if the view size changes.
Here is my implementation of the new method of my NSView:
func scroll(toRect rect: CGRect, animationDuration duration: Double) { if let scrollView = enclosingScrollView { // we do have a scroll view let clipView = scrollView.contentView // and thats its clip view var newOrigin = clipView.documentVisibleRect.origin // make a copy of the current origin if newOrigin.x > rect.origin.x { // we are too far to the right newOrigin.x = rect.origin.x // correct that } if rect.origin.x > newOrigin.x + clipView.documentVisibleRect.width - rect.width { // we are too far to the left newOrigin.x = rect.origin.x - clipView.documentVisibleRect.width + rect.width // correct that } if newOrigin.y > rect.origin.y { // we are too low newOrigin.y = rect.origin.y // correct that } if rect.origin.y > newOrigin.y + clipView.documentVisibleRect.height - rect.height { // we are too high newOrigin.y = rect.origin.y - clipView.documentVisibleRect.height + rect.height // correct that } newOrigin.x += clipView.bounds.origin.x - clipView.documentVisibleRect.origin.x // match the new origin to bounds.origin newOrigin.y += clipView.bounds.origin.y - clipView.documentVisibleRect.origin.y NSAnimationContext.beginGrouping() // create the animation NSAnimationContext.current.duration = duration // set its duration clipView.animator().setBoundsOrigin(newOrigin) // set the new origin with animation scrollView.reflectScrolledClipView(clipView) // and inform the scroll view about that NSAnimationContext.endGrouping() // finaly do the animation } }
Note that I use inverted coordinates in my NSView so that it matches iOS behavior. By the way, the animation duration in the iOS version of scrollRectToVisible is 0.3 seconds.
https://www.fpposchmann.de/animate-nsviews-scrolltovisible/
source share