Layer-Backed view no longer punches hole in transparent NSWindow - cocoa

I am trying to make a "hole" in an NSWindow using a CAShapeLayer or even just a CALayer.
When using regular NSViews or even layer-backed views, I can override drawRect: using code like this:
[spotImage drawInRect:self.bounds fromRect:NSZeroRect operation:NSCompositeXOR fraction:1.0];
where spotImage is an NSImage with pure white content and some gradations, and the window has a black background with 0.5 alpha. The NSView subclass where this drawRect is defined has a clearColor background.
The end result is a grey window (It is a transparent window with a styleMask of NSBorderlessWindowMask as can be found in many samples.
If I turn the NSView into a layer-backed view, it calls the drawRect methods and works fine.
When I turn this into a layer-hosting view, and again use the same structure (NSWindow > contentView > CustomView) then, the drawInRect method just draws the image. It no longer punches a hole through it.
It is like the layer itself can no longer punch the hole when it is part of a layer-hosting hierarchy.
Here is some sample code:
The custom NSWindow subclass initializer:
- (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)aStyle backing:(NSBackingStoreType)bufferingType defer:(BOOL)flag {
self = [super initWithContentRect:contentRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO];
if (self) {
[self setBackgroundColor: [NSColor lightGrayColor]]; //[NSColor clearColor]];
[self setAlphaValue:0.5];
[self setOpaque:NO];
[self setHasShadow: NO];
[self useOptimizedDrawing:YES];
[self setIgnoresMouseEvents:YES];
}
return self;
}
the code in my applicationDidFinishLaunching method:
PPContentView *thisView = [[PPContentView alloc]
initWithFrame:CGRectInset([self.window.contentView bounds], 50, 50)];
//[thisView setWantsLayer:YES]; enabling this makes things opaque again
[self.window.contentView addSubview:thisView];
thisView.layer.backgroundColor = [NSColor clearColor].CGColor;
//Create custom content
[thisView setNeedsDisplay:YES];
}
and my custom view's drawRect contains:
[[NSImage imageNamed:#"spotFuzzy.png"] drawInRect:self.bounds fromRect:NSZeroRect operation:NSCompositeXOR fraction:1.0];

Related

Making an NSImage out of an NSView

I know how to create an NSImage depicting an NSView and all its subviews, but what I'm after is an NSImage of a view ignoring its subviews. I can think of ways of doing this with a subclass of NSView, but I'm keen to avoid subclassing if possible. Does anyone have any ideas?
Hide the subviews, grab the image, unhide the subviews:
NSMutableArray* hiddenViews = [[NSMutableArray] alloc init];
for (NSView* subview in [self subviews]) {
if (subview hidden) [hiddenViews addObject: subview];
else [subview setHidden:YES];
}
NSSize imgSize = self.bounds.size;
NSBitmapImageRep * bir = [self bitmapImageRepForCachingDisplayInRect:[self bounds]];
[bir setSize:imgSize];
[self cacheDisplayInRect:[self bounds] toBitmapImageRep:bir];
NSImage* image = [[NSImage alloc] initWithSize:imgSize];
[image addRepresentation:bir];
for (NSView* subview in [self subviews]) {
if (![hiddenViews containsObject: subview])
[subview setHidden:NO];
}
I would suggest making a copy of the desired NSView offscreen and taking a snapshot of that.

How to draw on a transparent NSWindow with a transparent NSView?

I want to draw on the screen with a transparent background, so that everything (e.g. open applications are still visible).
In windowDidLoad of my custom NSWindowController i have the follwing:
[self.window setOpaque: NO];
[self.window setHasShadow:NO];
[self.window setBackgroundColor:[NSColor clearColor]];
[self.window setStyleMask:NSBorderlessWindowMask];
My custom NSWindow overrides canBecomeKeyWindow
- (BOOL)canBecomeKeyWindow {
return YES;
}
My View overrides drawRect
- (void)drawRect:(NSRect)rect {
[[NSColor clearColor]set];
NSRectFill(rect);
...
}
Problem: Trying to draw by using mouse events inside of my custom view results in the view/application beneath my Window to receive these events.
It justs works when i do not set the NSWindow styleMask to NSBorderlessWindowMask or by setting the background color of the custom view to semitransparent.
[[NSColor colorWithCalibratedRed:0 green:0 blue:0 alpha:0.05] set]
How can i draw on screen with full transparency and NSBorderlessWindowMask?
Adding
[self.window setIgnoresMouseEvents:NO];
to windowDidLoad of my custom NSWindowController solved it

NSScrollView background image in Lion

I'm working on a Mac application with an NSScrollView, and I want the NSScrollView to have a custom background image. I used this code in the custom documentView NSView subclass:
- (void)drawRect:(NSRect)rect {
[[NSColor colorWithPatternImage:[NSImage imageNamed:#"wood.jpg"]] set];
NSRectFill(rect);
}
That displays a pattern image as a background for the documentView.
But now in Mac OS X Lion, the NSScrollView bounces when scrolling further than possible, showing ugly white space. How can I make the white space also being covered by the background image?
Instead of overriding drawRect:, use the scroll view's setBackgroundColor: method, passing the NSColor you created with the pattern image.
You should subclass use NSScrollView setBackgroundColor, but then you should subclass NSClipView like this to pin the texture origin to the top:
#implementation MYClipView
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code here.
}
return self;
}
- (void)drawRect:(NSRect)dirtyRect
{
if (self.drawsBackground)
{
NSGraphicsContext* theContext = [NSGraphicsContext currentContext];
[theContext saveGraphicsState];
float xOffset = NSMinX([self convertRect:[self frame] toView:nil]);
float yOffset = NSMaxY([self convertRect:[self frame] toView:nil]);
[theContext setPatternPhase:NSMakePoint(xOffset, yOffset)];
NSColor* color = self.backgroundColor;
[color set];
NSRectFill([self bounds]);
[theContext restoreGraphicsState];
}
// Note: We don't call [super drawRect:dirtyRect] because we don't need it to draw over our background.
}
+ (void)replaceClipViewInScrollView:(NSScrollView*)scrollView
{
NSView* docView = [scrollView documentView]; //[[scrollView documentView] retain];
MYClipView* newClipView = nil;
newClipView = [[[self class] alloc] initWithFrame:[[scrollView contentView] frame]];
[newClipView setBackgroundColor:[[scrollView contentView] backgroundColor]];
[scrollView setContentView:(NSClipView*)newClipView]; [scrollView setDocumentView:docView];
// [newClipView release];
// [docView release];
}
#end
And call + (void)replaceClipViewInScrollView:(NSScrollView*)scrollView with the NSScrollView instance.
Put your drawRect code in an NSScrollView subclass. In IB, change the NSScrollView to use your custom subclass instead of NSScrollView. Also make sure to uncheck Draw Background in the scroll view's attributes inspector.

Create Drop Shadow for NSView - Cocoa

I'm trying to create a drop shadow surrounding the NSView like how NSWindow does it with its shadow, but I'm having some difficulty. I created a class for the NSView I'm creating the drop shadow for and I'm using this code for the overriding method:
-(void)drawRect:(NSRect)dirtyRect {
NSRect rect = NSInsetRect([self bounds], 10.0, 10.0);
NSShadow *dropShadow = [[[NSShadow alloc] init] autorelease];
[dropShadow setShadowColor:[NSColor blackColor]];
[dropShadow setShadowBlurRadius:5];
[dropShadow setShadowOffset:NSMakeSize(0,-3)];
[NSGraphicsContext saveGraphicsState];
[dropShadow set];
NSRectFill(rect);
[NSGraphicsContext restoreGraphicsState];
[super drawRect:dirtyRect];
}
This doesn't really create a drop shadow in which I'm looking.
Here is the shadow I'm trying to aim for...
rather creates a line through the NSView that seems like a border within the bounds of the view. Anyone got any ideas for this?
I have faced similar shadow issues because NSView clips its bounds.
I fixed it when I used a layer backed view. I simply set the superview's wantsLayer property to YES.. i.e [[view superView] setWantsLayer:YES] and set shadow for view [view setShadow:dropShadow].

Rounded rect on NSView that clips all containing subviews

I am creating a NSView subclass that has rounded corners. This view is meant to be a container and other subviews will be added to it. I am trying to get the rounded corners of the NSView to clip all of the subview's corners as well, but am not able to get it.
- (void)drawRect:(NSRect)dirtyRect {
NSRect rect = [self bounds];
NSBezierPath *path = [NSBezierPath bezierPathWithRoundedRect:rect xRadius:self.radius yRadius:self.radius];
[path addClip];
[[NSColor redColor] set];
NSRectFill(dirtyRect);
[super drawRect:dirtyRect];
}
The red is just for example. If I add a subview to the rect, The corners are not clipped:
How can I achieve this?
Using Core Animation layers will clip sublayers correctly.
In your container NSView subclass:
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.layer = _layer; // strangely necessary
self.wantsLayer = YES;
self.layer.masksToBounds = YES;
self.layer.cornerRadius = 10.0;
}
return self;
}
You can do it in the interface builder without subclassing adding User Defined Runtime Attributes"
Have you tried clipping with layers?
self.layer.cornerRadius = self.radius;
self.layer.masksToBounds = YES;
Ah, sorry, somehow I've missed that you were talking about NSView, not UIView. It would be hard to clip NSView subviews in all cases because it seems that most of Cocoa standard views set their own clipping path. It might be easier to layout subviews with some paddings and avoid need for clipping.

Resources