You've totally hit what I hit when Lion first came out, and it was my big WWDC 
question that year - took two Apple engineers digging in the code to figure it 
out; turned out to be a bug lurking since the NeXT days that only gets 
triggered when you add subviews to the scrollview and use overlay scrollers.

This is what we wrote at WWDC - it works on 10.6 & up:

- (void)awakeFromNib
{
   if (childView) {
      // Add it below the scroller so it doesn't draw over it.
      [self addSubview:childView
            positioned:NSWindowBelow
            relativeTo:[self verticalScroller]];
      [self tile];
   }
}

- (void)tile
{
   BOOL isLegacy = YES;
   if ([self respondsToSelector:@selector(scrollerStyle)]) {
      isLegacy = [self scrollerStyle] == 0; // NSScrollerStyleLegacy
   }
   if (isLegacy) {
      [self legacyTile];
   } else {
      [self overlayTile];
   }
}


- (void)overlayTile
{
   NSClipView *contentView = [self contentView];
   NSRect savedClipBounds = [contentView bounds];

   [super tile];
   if (!childView) {
      return;
   }

   NSRect contentFrame = [contentView frame];
   NSRect childFrame = [childView frame];
   childFrame.origin.y = NSMaxY(contentFrame) - NSHeight(childFrame);
   childFrame.size.width = contentFrame.size.width;
   [childView setFrame:childFrame];
   contentFrame.size.height = NSMinY(childFrame) - NSMinY(contentFrame);
   [contentView setFrameSize:contentFrame.size];

   // Fix adjusted scroll position.
   [contentView scrollToPoint:savedClipBounds.origin];
   [self reflectScrolledClipView:contentView];
}

- (void)legacyTile
{
   if (!childView) {
      [super tile];
      return;
   }

   NSSize viewSize = [self bounds].size;
   NSRect childFrame = [childView bounds];
   NSRect contentFrame = NSZeroRect;
   NSClipView *contentView = [self contentView];

   // Adjust content for child view.
   childFrame.origin.x = childFrame.origin.y = 0;
   contentFrame.origin.x = 0;
   contentFrame.size.height = viewSize.height - childFrame.size.height;
   contentFrame.size.width = viewSize.width;

   if ([self isFlipped]) {
      childFrame.origin.y = viewSize.height - childFrame.size.height;
      contentFrame.origin.y = 0;
   } else {
      contentFrame.origin.y = childFrame.size.height;
   }

   /*
    * Adjust scrollers for the new content view size,
    * allowing for scroller space.
    */
   BOOL hasHScroll = [self hasHorizontalScroller];
   BOOL hasVScroll = [self hasVerticalScroller];
   NSScroller *verticalScroller = [self verticalScroller];
   NSScroller *horizontalScroller = [self horizontalScroller];
   NSRect insetContentFrame = contentFrame;

   NSRect hscrollRect = hasHScroll ? [horizontalScroller frame] : NSZeroRect;
   NSRect vscrollRect = hasVScroll ? [verticalScroller frame] : NSZeroRect;
   CGFloat vScrollWidth = vscrollRect.size.width;
   CGFloat hScrollHeight = hscrollRect.size.height;

   insetContentFrame.size.width -= vScrollWidth;
   insetContentFrame.size.height -= hScrollHeight;

   /*
    * Force a layout at the proposed new size so we'll know
    * whether we'll need scrollbars for the documentRect
    */
   NSSize oldContentSize = [contentView bounds].size;
   if (!NSEqualSizes(oldContentSize, insetContentFrame.size)) {
      [contentView setFrame:insetContentFrame];
   }
   NSRect docRect = [[self contentView] documentRect];

   BOOL showVScroll =
      hasVScroll && insetContentFrame.size.height < docRect.size.height;
   BOOL showHScroll =
      hasHScroll && insetContentFrame.size.width < docRect.size.width;

   if (showVScroll) {
      [verticalScroller setHidden:NO];
      vscrollRect.size.height = viewSize.height;
      vscrollRect.origin.x = viewSize.width - vscrollRect.size.width;
      vscrollRect.origin.y = 0;
      if (showHScroll) {
         vscrollRect.size.height -= hScrollHeight;
         if (![self isFlipped]) {
            vscrollRect.origin.y = hScrollHeight;
         }
      }
      [verticalScroller setFrame:vscrollRect];
      contentFrame.size.width = insetContentFrame.size.width;
   } else {
      [verticalScroller setHidden:YES];
   }

   if (showHScroll) {
      [horizontalScroller setHidden:NO];
      hscrollRect.size.width = viewSize.width;
      hscrollRect.origin.x = 0;
      if ([self isFlipped]) {
         hscrollRect.origin.y = viewSize.height - hScrollHeight;
         childFrame.origin.y -= hScrollHeight;
      } else {
         hscrollRect.origin.y = 0;
         childFrame.origin.y += hScrollHeight;
      }
      if (showVScroll) {
         hscrollRect.size.width -= vScrollWidth;
      }
      [horizontalScroller setFrame:hscrollRect];
      contentFrame.size.height = insetContentFrame.size.height;
   } else {
      [horizontalScroller setHidden:YES];
   }

   // Set the final new size.
   childFrame.size.width = contentFrame.size.width;
   [childView setFrame:childFrame];
   [contentView setFrame:contentFrame];
}

----- Original Message -----
From: "Gideon King" <gid...@novamind.com>
To: "Graham Cox" <graham....@bigpond.com>
Cc: "Cocoa-Dev List" <cocoa-dev@lists.apple.com>
Sent: Saturday, July 7, 2012 7:13:53 PM
Subject: Re: Problem adding subview to NSScroller subclass

Yes, I was using that type of code before too, but it didn't work with the new 
scrollbar styles (drawing artifacts on resize, not automatically hiding), which 
is what prompted me to look at subclassing the scroller itself instead. 
Unfortunately it still only works with the legacy style scrollers and not 
overlays as per my previous message.

Regards

Gideon

On 08/07/2012, at 11:04 AM, Graham Cox <graham....@bigpond.com> wrote:

> 
> On 07/07/2012, at 6:38 PM, Gideon King wrote:
> 
>> Has anybody successfully added a subview to an NSScroller?
> 
> 
> Yes, but more recently I took it out again and moved that extra view 
> elsewhere, because on Lion/Mountain Lion, these scroll areas are handled 
> differently and the presence of an extra view is detected and used to revert 
> the scroller to the "legacy" scrollbar design, which is going to look 
> increasingly out of place as apps adopt the new one. This extra 
> special-casing could also be affecting whether and how your view is drawn. 
> That said my code worked on Lion, it just used the legacy scrollbars.
> 
> My subclass of NSScrollView only overrides one method - tile, and adds a 
> property, 'placard' which is the extra view to insert. I believe this code 
> originally was derived from someone else's example I found on the web in 
> about 2003, so I'm not trying to claim it as my own. It does work however.
> 
> 
> --Graham
> 
> 

_______________________________________________

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/lrucker%40vmware.com

This email sent to lruc...@vmware.com
_______________________________________________

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Reply via email to