How can I get NSScrollView to respect a clipping path - cocoa

I am trying to make a NSScrollView with clipped corners, similar to the Twitter app:
I have a NSScrollView subclass which I added the following code:
- (void)drawRect:(NSRect)dirtyRect {
NSBezierPath *pcath = [NSBezierPath bezierPathWithRoundedRect:[self bounds] xRadius:kDefaultCornerRadius yRadius:kDefaultCornerRadius];
[path setClip];
[super drawRect:dirtyRect];
}
I expected the content of the NSScrollView to have rounded corners, but it is not respecting the clipped path. How can I do this?
UPDATE & CLARIFICATION
I know how to make a custom NSScroller, I know how to make it transparent overlay. All I am asking is how to make the NSSCrollView clip its corners, including everything it contains. The NSScrollView is inside a NSView which has a background that could change, meaning a view overlay to fake the rounded corners is not an option.

After much fiddling, I just discovered that NSScrollView's can be made to have rounded corners by simply giving it a backing layer and setting that layer's corner radius provided you also do the same to it's internal NSClipView. Both are required, which now makes sense, since it's the clip view that actually provides the viewable window into the NSScrollView's document view.
NSScrollView * scrollView = ...;
// Give the NSScrollView a backing layer and set it's corner radius.
[scrollView setWantsLayer:YES];
[scrollView.layer setCornerRadius:10.0f];
// Give the NSScrollView's internal clip view a backing layer and set it's corner radius.
[scrollView.contentView setWantsLayer:YES];
[scrollView.contentView.layer setCornerRadius:10.0f];

You can apply a mask to a view's layer:
[myScrollView setWantsLayer: YES];
[myScrollView layer].mask = ...;
The mask is another CALayer. So in this case you'd create a CALayer, set its background colour to opaque, set its bounds to match the scrollview, and give it a cornerRadius of, say, 8.0.
The result would be that the scroll view and all its contents would appear to be masked to a roundrect with a corner radius of 8px.

Have you tried overriding
- (BOOL)isOpaque {
return NO;
}
And setting the scroll view's -setDrawsBackground: to NO and just leave the view without clipping and just draw the corners with [NSColor clearColor] since this will also clear the underlying color and simulate a round effect.

Related

What is the best way to show the content below the NSWindow?

Here is the UI Mock up:
The blue background is the screen, and the red is a NSWindow, and I would like to build a NSView? (Yellow) that is on top of the NSWindow. The NSView is basically transparent, so that it can shows the live information behind the red NSWindow. What is the best way to implement the NSView? Any suggestions? Thanks.
***One more remarks, the Yellow view is not only need to transparent, but also need to use for analysis. So, if I can get the raw data or transfer it to NSImage will be great.
My 2 cents. Let your view be an NSImageView. You can draw the background to be clear
- (void)drawRect:(NSRect)rect
{
[[NSColor clearColor] set];
NSRectFill(rect);
}
You could then convert whatever you draw in there into an image by using [NSBitmapImageRep initWithFocussedViewRect:]. Would something like that work?

NSView with layer is drawn over all content

I have an NSView, which I change some properties to give make it a rounded rectangle, with a gray color, and add it to my view (behind everything)
float gray = 60.0f/255.0f;
NSView* background = [[NSView alloc] initWithFrame: self.iconContainer.frame];
CALayer *viewLayer = [CALayer layer];
[viewLayer setBackgroundColor:CGColorCreateGenericRGB(gray, gray,gray, 1)]; //RGB plus Alpha Channel
[viewLayer setCornerRadius:5.0f];
[background setWantsLayer:YES]; // view's backing store is using a Core Animation Layer
[background setLayer:viewLayer];
// Place view behind all other views
[self.iconContainer addSubview:background positioned:NSWindowBelow relativeTo:nil];
However no matter what I try, that particular view is drawn above everything else.
Usually, this behaviour is caused by the use of Core Animation layers on a subview, while the superview hasn't any.
Enabling Core Animation layers on the superview should fix the issue, as any subviews will then be drawn using Cora Animation layers, making the drawing process respect the order of subviews.

Convert NSGradient to NSColor

Is it possible to convert an NSGradient to an NSColor
- (void) viewWillDraw {
NSGradient *grad = [[NSGradient alloc] initWithStartingColor:[NSColor lightGrayColor] endingColor:[NSColor darkGrayColor]];
[super setBackgroundColor:*gradient*;
}
This is my method, I want to be able to pass the NSGradient in as an NSColor, which obviously i cant, is there any way to convert it to one?
On 10.8, you can create, in the following order:
A block that draws the gradient however you like.
An image that is backed by the block.
A color that repeats the image as a pattern.
In this way, you can create a color that looks like anything, including a gradient.
That said, this may not work correctly with window resizing if you try to have the gradient adapt to the size of the background (by using the rect passed to the block) and the background is of a text view in a scroll view. (When I tried it awhile back, the pattern didn't redraw the block; it simply tiled, which looked weird in at least one dimension.) If either your gradient or your window is fixed in size, then you will not have that problem.
NSGradient is not Convertible to NSColor.
The NSGradient class provides support for drawing gradient fill
colors, also known as shadings in Quartz. This class provides
convenience methods for drawing radial or linear (axial) gradients for
rectangles and NSBezierPath objects.
As you want to set the viewBackground to to an effect (Gradient effect) you need to do as:
[grad drawInRect:<the rect of your view> angle:270]; //angle is upto your requirement,

NSView free-angle rotation

I have NSView container (with NSImageView and some other custom subviews). How to set its rotation properly? I tried to set angle through setFrameRotation: and set rotation matrix in views layer. But in these cases subview image becomes downscaled and clipped.
Update:
If I set rotation via [myView setFrameRotation: angle]:
almost fine, except text frame (drawing via [NSString drawAtPoint:...] and rotation anchor is at left-bottom corner (I want at bottom-center, [myView setFrameOrigin:...] does nothing)
If I set rotation via myView.layer.transform = CATransform3DMakeRotation (angle, 0, 0, 1):
frame bound remains unrotated and clips subviews (but this approach is more suitable for view container)

Clipping rounded corners on a NSScrollView

I have a simple custom borderless NSWindow subclass which has a rounded rectangle shape.
In the content view of this window, I've added an NSScrollView.
How do I get the NSScrollView to clip its document view to the rounded rectangle shape of the NSWindow?
I've tried subclassing the NSScrollView, overriding drawRect: and adding a clipping path before calling super. I've also tried subclassing the document view and the clip view with the same technique but I cannot get it to clip.
BTW, this is on Lion with the elastic scrolling behaviour.
After much fiddling, I just discovered that NSScrollView's can be made to have rounded corners by simply giving it a backing layer and setting that layer's corner radius provided you also do the same to it's internal NSClipView. Both are required, which now makes sense, since it's the clip view that actually provides the viewable window into the NSScrollView's document view.
NSScrollView * scrollView = ...;
// Give the NSScrollView a backing layer and set it's corner radius.
[scrollView setWantsLayer:YES];
[scrollView.layer setCornerRadius:10.0f];
// Give the NSScrollView's internal clip view a backing layer and set it's corner radius.
[scrollView.contentView setWantsLayer:YES];
[scrollView.contentView.layer setCornerRadius:10.0f];
Even better IMO:
scrollView.wantsLayer = true
scrollView.layer?.masksToBounds = true
scrollView.contentView.wantsLayer = true
scrollView.contentView.layer?.masksToBounds = true
In Swift I have solved like this:
scrollView.wantsLayer = true
scrollView.contentView.wantsLayer = true
scrollView.layer?.cornerRadius = 20.0
scrollView.contentView.layer?.cornerRadius = 20.0

Resources