I recently ran into a situation where I wanted to significantly customize the behavior of a scrollbar in a Cocoa WebView
and was disappointed in how little access I had to it. There are two factors that complicated my situation. First was the fact that the text I was displaying in the WebView
was being loaded dynamically. As the user scrolls up or down, Javascript methods call into my Objective-C code to request additional blocks of text to display either above or below the text already displayed. As the user scrolls up (or down), blocks that are “too far” below (or above) the displayed text are removed by the Javascript on the page. As a result, the built-in WebView
scrollbar is displaying completely irrelevant information.
Second, the feature of OS X 10.7+ that allows the user to control whether or not scrollbars are always displayed interfered with my attempts to simply drop an NSScroller
on top of the region of the WebView where the scrollbar appears. If the user turned on “overlay scrollbars”, the WebView
would re-wrap the text so that it continued under my scrollbar. On top of that, when the system drew its scrollbar, it would draw it on top of mine, even though mine was on top of the WebView
in z-order. Furthermore, the WebView
would “scroll” the pixels on my scrollbar in the process of scrolling its own text.
It was a mess.
The solution is fairly simple, though:
- Drop an
NSScroller
instance over the right side of theWebView
in Interface Builder. The width doesn’t matter as we’ll adjust it in-awakeFromNib
to match the width of a standard-sizedNSScroller
. - In the window’s
-awakeFromNib
method, enable theNSScrollbar
instance, set its size toNSRegularControlSize
, and set its initial knob size and position. Also set theWebView
‘s scroller style to “legacy”:
[[[[[webView mainFrame] frameView] documentView] enclosingScrollView] setScrollerStyle:NSScrollerStyleLegacy];
- As blocks are loaded and the Javascript reports its position within the displayed text, update the position of the knob.
- Connect the
NSScroller
action to a handler in the code to position the text appropriately with changes to the knob. - Add a handler to intercept distributed notifications:
[[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(observeDistributedNotifications:) name:nil object:nil];
- In the handler for the distributed notifications, look for the notification named
AppleShowScrollBarsSettingChanged
and when it occurs, call a method that forces theWebView
scrollbar back to the “legacy” style. You must call this method on a short delay because notifications are not delivered in any particular order. You need to allow the system to change theWebView
scroller style before you change it back.
That’s it. The basic idea is to provide your own NSScroller
that is updated by position changes in the WebView
and which moves the WebView
as the user interacts with it. This scrollbar lays on top of the WebView
scroller so the user can’t see it. And to keep OS X from inappropriately drawing, we intercept system preference changes that would alter the style of the WebView
scroller.
I think this solution would work with any Cocoa view object that is based on NSScrollView
. One notable side-effect is that your scrollbar does not auto-hide. In my case, I can live with this.
Hi Craig,
I just came across your post, and was wondering if you might be able to answer a possibly-related question: Is there a Terminal command I could use to increase the default (system-defined) width of the scroll bars in Yosemite? They’re significantly narrower than they were in Snow Leopard, which makes it harder to grab them with the mouse.
If one sets “Show scroll bars” to “Automatically…” or “When scrolling,” one can get behavior in which they briefly become wider, but this is not useful to me because (a) it’s transient and; and (b) I want mine set to always display.
[And, BTW, I own five pairs of VFF’s, and agree with what you wrote about the settlement.]
The best I can suggest is to turn them on under “General” in preferences. You can choose to always show scrollbars. I think they may be a little narrower than they used to be, but at least they don’t disappear while you’re trying to grab them.
The move in Windows and OS X to hide elements of the user interface (Windows is HORRIBLE in this regard) is inarguably wrong. There isn’t a design principle it doesn’t vioate. It throws away everything we’ve learned in the last 35 years about user interface design. The idea that I have to scroll to see how to scroll should have been laughed down the moment the second programmer saw it implemented. The fact that it wasn’t betrays the sad state of affairs in software development today.
Thanks Craig, but I think you may have misunderstood my question. I do know how to make them always display– I simply have “Show scroll bars” set to “Always.” My issue is that they are too narrow, so I’m looking for a Terminal command I can use to increase their width.
[I only mentioned the behavior under “Automatically…” or “When scrolling” to indicate that they can be made wider, but only temporarily, and only when using these settings, which I don’t like because I prefer “Always.”]
I understood your question. I haven’t seen any way to make them bigger. That’s why I said “the best I can suggest is to turn them on”. They’re a little wider when always on than they are when they automatically show/hide.
BTW, regarding hidden elements, there’s also the Hide/Show option for the Favorites in the Finder sidebar, that can only be seen if you know to hover your cursor over the blank space at the upper right of the sidebar, just opposite the word “Favorites.” Be great if I could get a list of all of these.
Ah, I see.
Someone on another site just mentioned the Firefox Add-on, NewScrollBars, which allows you to set to them to whatever width you like, thus overriding what OS X does. Of course, this is only in FF, and I’m looking for something universal.
Update: there may be a way, by editing SystemAppearance.car (where the scrollbar graphics are stored) using Alex Zielenski’s ThemeEngine (https://github.com/alexzielenski/ThemeEngine). See also http://junesgraphics.blogspot.com/2015/03/theming-yosemite-part-2-editing-car.html#.VYmb12BASS0 .
I haven’t had a chance to play with this myself yet, but thought you might find it interesting.