3

In Mavericks, Apple introduced responsive scrolling architecture in NSScrollView. This feature adds a smart way for generating overdraws and decouples handling of the incoming scroll events from the main event loop into a separate one. This topic is described in detail in the session 215 from WWDC 2013.

Because of the different event model, the scroll events no longer go through the scrollWheel(with:) method. In fact, if you override this method in the NSScrollView subclass, you opt-out from the responsive scrolling architecture completely and fall back to the legacy model.

In my NSScrollView I'd like to implement an interaction for magnification using scrolling while holding the command key. This can be observed in MindNode or OmniGraffle apps. The standard way to do this would be overriding scrollWheel(with:) and checking the modifierFlags on each scroll event. As described above, this would cause an opt-out from the responsive scrolling model.

I wonder whether there is a way to implement this interaction while also preserving the responsive scrolling?


What I have already achieved/tried:

  • In my NSScrollView subclass I have overridden scrollWheel(with:) and am returning true from the static property isCompatibleWithResponsiveScrolling in order to force the participation in responsive scrolling.
  • This way I am able to check the first scroll event for the modifier flags. If the command key is NOT pressed, I simply pass the event to super and let the NSScrollView do its thing. If the command key is pressed, I go a different route and track next scroll events on the window to do the magnification.
  • The problem is when one of these tracking loops is running and the user changes the press state of the command key (either presses it or releases it).
  • The switch from magnification to scrolling (on command key release) is simple, since the tracking loop is fully under my control.
  • The switch from scrolling to magnification (on command key press) is more tricky, because I cannot check the scroll events. I have overridden flagsChanged(with:) and can observe when this moment happens but I have not found a way to end the scrolling. This SO question asks about ending/disabling scrolling but has no answer yet.
Lukáš Kubánek
  • 946
  • 1
  • 15
  • 27
  • I’m +1ing this — I ended up just giving up on responsive scrolling, and making sure to handle the “momentum” scrollWheel events specially so that momentum from scrolling wouldn’t bleed over into momentum zooming if the user let up on the key in the middle. – Wil Shipley Apr 21 '19 at 23:35
  • @WilShipley Thanks for +1ing. What kind of app are you working on? I heavily rely on drawing to the canvas view within the scroll view in Diagrams (https://diagrams.app) and I fear that giving up on responsive scrolling would affect the performance. Is it noticeable in your case? – Lukáš Kubánek Apr 29 '19 at 08:43

1 Answers1

0

I’m going to answer here instead of in comments, since I have more data.

There are two issues with “Responsive scrolling”:

(1) A few years ago there was a bug — a difference in how accelerated responsive scrolling was vs. legacy scrolling. By accelerated I mean the distance the document would travel for any given amount of movement of the fingers on the trackpad — legacy scrolling didn’t move the document as much so it felt sluggish. I reported this and it seems to have been fixed in 10.14, at least.

⑵ As near as I can tell from my research and talking to Apple people “Responsive scrolling” was designed to make scrolling more consistent even when an application is hogging the main thread (which would normally block the flow of events) — it processes scroll wheel events on a background thread instead and then messages the main thread with them. Since this still involves the main thread being called per scrolling event (or scrolling events being coalesced) I’m not clear what situations this is a win in. There may be some extra magic where the scrollview will draw more of the document from the cached content of you implement the optional caching stuff.

In my app I’m actually using the scrollview with a dummy (transparent) document and I take its scroll events and move around a SceneKit camera. Since my scene changes dramatically as you scroll around there’s no benefit to trying to pre-render outside the visible rect.

So in my case I no longer perceive any performance difference between responsive and legacy scrolling (although I did back when I filed the bug).

Wil Shipley
  • 9,343
  • 35
  • 59
  • Thanks for your further research, Will. Now that you’ve turned responsive scrolling off as well as folks at MindNode did, I also decided to try disabling it to make my development life easier. But the performance suffers so much and it feels really sluggish, probably because of all the drawing I’m doing. Enabling it makes the scrolling butter smooth again (as advertised by Craig Federighi) so I’m on a new path to find a solution to this problem. – Lukáš Kubánek Jun 01 '19 at 09:48
  • I filled [a radar](https://openradar.appspot.com/radar?id=4987454423564288) and a friend of mine will go to the WWDC labs with this problem and asks Apple engineers. What I need is basically a way for forcing the responsive scroll tracking loop to end programatically... – Lukáš Kubánek Jun 01 '19 at 09:48