6
votes

I have a NSScrollView, which was set to:

MyNSScrollView.hasHorizontalScroller = YES;
MyNSScrollView.hasVerticalScroller = YES;
MyNSScrollView.autohidesScrollers = YES;
MyNSScrollView.scrollerStyle = NSScrollerStyleOverlay;

I noticed that when if there's no trackpad connecting to OS X, and by default, the NSScrollView will ignore my settings in the code and force the scrollers always shown:

Settings

I can only either change my system settings to "When scrolling" or set hasHorizontalScroller etc. to NO to hide it, and the later will disable mouse scrolling which is not the result I want.

By default (Automatically based on mouse or trackpad) will always display the scroller if the user has no trackpad, even when the content size does not exceed the frame size. But if you have a trackpad, it will be overlay style that no matter the scroller shows or not, it's above the content.

The difference between the 2 is that "legacy" style will take up spaces in the scrollerview. It'd be a problem if you were relaying on the visiableRect value for calculation, or your contents needs to remain certain aspect-ratio via constraints.

Is there a way to force hide them without disabling them?

2
I suggest you read what you have written for yourself. "it will ignore my settings in the program and force them always shown" => What is it? Your application? What are they? scroll bars? - El Tomato
there are 2 scroller bars, horizontal and vertical, that why I said them as I didn't mention other things but those 2 bars. I'm sorry if it does not sound clear enough. - Cai
Ignore El Tomato, he's a bit grumpy lately. - Willeke
Instead of ignoring the users prefs, why don't you find a way to support the scrollers? - Willeke
As the scroller size could be different on different system, it could be too many unpredictable situations. I'm thinking about getting the scroller frame and resize it to zero width/height. - Cai

2 Answers

1
votes

You have not been clear about exactly what symptoms occur under what circumstances. For example, what is your normal setting for "Show scroll bars:" in that preference pane? What do you want the behavior of the scrollers to be? Always visible? Only show when scrolling?

In any case, I think the issue is that you simply are misunderstanding what autohidesScrollers does. Setting that to true simply means that the scrollers are hidden when the document view does not extend past the bounds of the clip view (a.k.a. content view). That is if there's no place to scroll to because everything is already showing.

That property has nothing to do with the scrollers being visible always or only when scrolling or whatever. That's a system setting that you can't override programmatically. All scrollers behave the same throughout all apps in the user session.

0
votes

You can force the whole app to use overlay scrollers by using some low-level Objective-C magic (method swizzling):

#import <Cocoa/Cocoa.h>
#import <objc/runtime.h>

static IMP old_preferredScrollerStyle = NULL;
static NSScrollerStyle new_preferredScrollerStyle(id self, SEL _cmd) {
    // Always prefer overlay style.
    return NSScrollerStyleOverlay;
}

static IMP old_setScrollerStyle = NULL;
static void new_setScrollerStyle(id self, SEL _cmd, NSScrollerStyle style) {
    // Call old implementation but always with overlay style.
    void(*oldImp)(id self, SEL _cmd, NSScrollerStyle style)
        = (void(*)(id, SEL, NSScrollerStyle))old_setScrollerStyle;
    oldImp(self, _cmd, NSScrollerStyleOverlay);
}

/// Force the overlay style scrollers for this app.
@interface NSScrollView (ForceOverlay)
@end

@implementation NSScrollView (ForceOverlay)

+ (void)load
{
    [super load];

    // Replace the preferred style. This sets the style for app startup and new NSScroller
    // and NSScrollView instances.
    Method originalMethod = class_getClassMethod(
        [NSScroller class],
        @selector(preferredScrollerStyle)
    );
    old_preferredScrollerStyle = method_setImplementation(
        originalMethod,
        (IMP)new_preferredScrollerStyle
    );

    // Replace the NSScrollView setter. This prevents the change to the legacy style, for example
    // when the user switches the system setting.
    originalMethod = class_getInstanceMethod(
        [NSScrollView class],
        @selector(setScrollerStyle:)
    );
    old_setScrollerStyle = method_setImplementation(
        originalMethod,
        (IMP)new_setScrollerStyle
    );
}

@end