Clifton Labrum
Clifton Labrum

Reputation: 14068

NSTextView inside NSTableView Blocks Scrolling

I have an NSTableView and within each table row there is an NSTextView. I have disabled scrolling on the NSTextView in my storyboard. I can scroll my NSTableView just fine, but when my mouse cursor is over the top of the NSTextView, the scrolling stops. I assume this is because the NSTextView is intercepting the scroll behavior.

The arrow points to the NSTextView:

Arrow points to the NSTextView

Keep in mind that the NSTextView is in a subclassed NSTableViewCell.

class AnnouncementsVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource {
  @IBOutlet weak var tableView: NSTableView!

  override func viewDidLoad() {
    //...
  }
  func numberOfRows(in tableView: NSTableView) -> Int {
    //...
  }
  func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
    //...
  }
}

class AnnouncementTableCell: NSTableCellView{
  @IBOutlet weak var message: NSTextView!
}

How do I make the NSTextView pass its scroll events up to its parent NSTableView? My guess is I will need scrollWheel(with event: NSEvent) but I'm unsure where it goes since I have two different classes at play here.

Upvotes: 1

Views: 460

Answers (2)

Clifton Labrum
Clifton Labrum

Reputation: 14068

Here's a Swift (4.2) version that works great:

class MyScrollClass: NSScrollView{
  override func scrollWheel(with event: NSEvent) {
    self.nextResponder?.scrollWheel(with: event)
  }
}

Just add that sub-class your NSScrollView and it should work.

Upvotes: 2

arri
arri

Reputation: 391

One way to achieve this would be to subclass NSScrollView and implement scrollWheel: to conditionally (1) either consume scroll-events in your subclass-instance as needed, or (2) forward them to an enclosing scrollview, based on your instances' current scrollposition.

Here's a rather rudimentary implementation (Obj-C) that i've used in the past:

- (void)scrollWheel:(NSEvent *)e {


    if(self.enclosingScrollView) {

        NSSize
        contSize = self.contentSize,
        docSize = [self.documentView bounds].size,
        scrollSize = NSMakeSize(MAX(docSize.width  - contSize.width, 0.0f),
                                MAX(docSize.height - contSize.height,0.0f) );

        NSPoint
        scrollPos = self.documentVisibleRect.origin,
        normPos = NSMakePoint(scrollSize.width  ? scrollPos.x/scrollSize.width  : 0.0,
                              scrollSize.height ? scrollPos.y/scrollSize.height : 0.0 );

        if( ((NSView*)self.documentView).isFlipped ) normPos.y = 1.0 - normPos.y;

        if(   ( e.scrollingDeltaX>0.0f && normPos.x>0.0f) 
           || ( e.scrollingDeltaX<0.0f && normPos.x<1.0f)
           || ( e.scrollingDeltaY<0.0f && normPos.y>0.0f)
           || ( e.scrollingDeltaY>0.0f && normPos.y<1.0f) ) {

            [super scrollWheel:e];

        }
        else

            [self.nextResponder scrollWheel:e];

    }
    else 
        // Don't bother when not nested inside another NSScrollView
        [super scrollWheel:e];
}

It still leaves much to be desired, like handling the deltaX and deltaY components independantly, but perhaps it's sufficient for your case.

Upvotes: 1

Related Questions