How to Disable NSScrollView Scrolling

Unlike UIScrollView on iOS, NSScrollView on Mac does not have a handy scrollEnabled property for you to set to NO if you need to temporarily disable scrolling in your app.

If you Google around, you can find several posts on websites like stackoverflow that ask questions like How can I disable the vertical scrolling of a NSScrollView? Some have suggested setting the hasVerticalScroller property to NO as the answer. However, the hasVerticalScroller property only effects the visibility of the scroller, a value of NO does not actually prevent scrolling.

There are also ways to constrain scrolling from an NSView subclass, as described in Apple’s documentation here. Constraining scrolling from a view subclass can be useful, but what if you want to just temporarily disable scrolling?

The easiest way to disable scrolling it seems is to subclass NSScrollView and add a BOOL property with a name like scrollingEnabled. Then you can simply override the designated initializers (initWithFrame: and initWithCoder:) and set the ivar of the property to YES as the default value:

-(instancetype)initWithFrame:(NSRect)frameRect 
{ 
   self = [super initWithFrame:frameRect]; 
   if (self) 
   { 
       [self setUpOnInit]; 
   } 
   return self; 
}
 
- (instancetype)initWithCoder:(NSCoder *)coder 
{ 
    self = [super initWithCoder:coder]; 
    if (self) 
    { 
         [self setUpOnInit]; 
    } 
    return self; 
}
 
-(void)setUpOnInit
{
   //Set all default values. 
   _scrollingEnabled = YES; 
}

Now you can just override the scrollWheel: method and check the property:

-(void)scrollWheel:(NSEvent *)theEvent 
{ 
    if (self.scrollingEnabled) {  
      [super scrollWheel:theEvent];  
     } 
    else {  
       //scrolling is disabled. 
    } 
}

This technique will work in most cases, but there may be times when a view inside of a scroll view implements autoscrolling behavior (perhaps if the view is a dragging destination). If you need to temporarily disable scrolling in such a case, you should also subclass NSClipView and block scrolling by overriding the -constrainBoundsRect: method like this:

-(NSRect)constrainBoundsRect:(NSRect)proposedBounds
{
    MyScrollViewSubclass *mainScrollView = (MyScrollViewSubclass*)self.superview;
 
    if (mainScrollView.scrollingEnabled)
    {
        return [super constrainBoundsRect:proposedBounds];
    }
    else
    {
        //Disabled
        return self.documentVisibleRect;
    }
}