Re: Problem adding subview to NSScroller subclass
Re: Problem adding subview to NSScroller subclass
- Subject: Re: Problem adding subview to NSScroller subclass
- From: Lee Ann Rucker <email@hidden>
- Date: Sat, 07 Jul 2012 22:04:55 -0700 (PDT)
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" <email@hidden>
To: "Graham Cox" <email@hidden>
Cc: "Cocoa-Dev List" <email@hidden>
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 <email@hidden> 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 (email@hidden)
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:
This email sent to email@hidden
_______________________________________________
Cocoa-dev mailing list (email@hidden)
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:
This email sent to email@hidden