Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NSScrollView: Override system display settings?

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?

like image 240
Cai Avatar asked Jun 11 '16 02:06

Cai


2 Answers

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
like image 123
DarkDust Avatar answered Nov 18 '22 23:11

DarkDust


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.

like image 1
Ken Thomases Avatar answered Nov 19 '22 01:11

Ken Thomases