NSView wont use CALayer at all - cocoa

I've tried everything and read a LOT about using a CALayer in a NSView, but can't get code that worked fine on iOS to work on a Mac.
I'm calling
[ nsView setLayer:[CALayer layer]];
[ nsView wantsLayer];
to create a layer as I've read everywhere I should.
then I've tried settings the layer's background color, and adding a sublayer and setting it's background color, but I just get a black window no matter what I do.
The entire programatically created app is, after removing all nibs etc, trying to make a gradient to be sure I get some Layer content:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
NSWindow *mainWindow;
NSView *nsView;
mainWindow = [[NSWindow alloc] initWithContentRect:NSMakeRect(20, 500, 200, 400)//screenRect
styleMask:NSTitledWindowMask
backing:NSBackingStoreBuffered
defer:NO];
[mainWindow setTitle:#"My Window"];
CAGradientLayer *layer;
nsView = [ [ NSView alloc] initWithFrame:NSMakeRect(20, 20, 200, 400)];
[mainWindow makeKeyAndOrderFront:nil];
[ nsView setLayer:[CALayer layer]];
[ nsView wantsLayer];
[ nsView setLayerContentsRedrawPolicy:NSViewLayerContentsRedrawOnSetNeedsDisplay];
layer = [CAGradientLayer layer];
layer.frame = CGRectMake(0, 0, 200, 400);
layer.colors = #[ (id)CGColorCreateGenericRGB(1, 0, 0, 1), (id)CGColorCreateGenericRGB(1, 1, 0, 1) ];
layer.startPoint = CGPointMake(0.5, 0);
layer.endPoint = CGPointMake(0.5, 1);
[nsView.layer addSublayer:layer];
[layer setNeedsDisplay];
[nsView setNeedsDisplay:YES];
[mainWindow setContentView:nsView];
}
no matter what I do, I never get any indication that the layer is being used. It and subviews exists when I debug, but are never shown. The view's drawRect method is called once as well.
I've read for days about using CALayers for NSViews, but nothing seems to get them working.
Any suggestions?

It was something amazingly simple but easy to be blind about:
I was calling:
[nsView wantsLayer]
(thinking it was a way of setting wantsLayer (probably infuenced by seeing it as a property)).
Which is just a getter that was being ignored.
[nsView setWantsLayer:YES]
or
nsView.wantsLayer = YES;
would of course be what I wanted.

Related

Why is my layer shadow clipped to my NSView's rect?

I'm creating a window containing an NSButton (both window content view & button have wantsLayer = YES), and setting the NSButton's shadowColor, shadowRadius, shadowOpacity and shadowOffset. But my shadow gets clipped to the rect of the NSView. How do I fix this?
NSRect wdBox = NSMakeRect(0,0,100,100);
NSWindow * theWindow = [[[NSWindow alloc] initWithContentRect: wdBox styleMask: NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask backing: NSBackingStoreBuffered defer: NO] autorelease];
NSView* cv = [[[NSView alloc] initWithFrame: wdBox] autorelease];
cv.wantsLayer = YES;
[cv setLayerUsesCoreImageFilters: YES];
theWindow.contentView = cv;
[theWindow setCollectionBehavior: NSWindowCollectionBehaviorFullScreenPrimary];
[theWindow setTitle: #"foo"];
NSButton* mView = [[NSButton alloc] initWithFrame: NSMakeRect(10, 10, 100, 80)];
[mView setLayerUsesCoreImageFilters: YES];
[mView setWantsLayer: YES];
mView.layer.masksToBounds = NO;
[mView.layer setShadowColor: [NSColor.redColor CGColor]];
[mView.layer setShadowOffset: CGSizeMake(4, 4)];
[mView.layer setShadowRadius: 8];
[mView.layer setShadowOpacity: 1.0];
[mView setBezelStyle: NSRoundRectBezelStyle];
[mView setTitle: #"bar"];
[theWindow.contentView addSubview: mView];
Here's a picture:
I had similar code before and it used to work until 10.9, but the shadow suddenly got clipped to the view, so I rewrote it to this simple case, but it's still clipped.
There is a bug in Mavericks. If you create an NSButton in XIB and give it a layer and set its shadow it works fine, but if you create one programmatically it clips its shadow.
I suspect the AppKit team did some crazy hacks when they made buttons do fast layer compositing in Mavericks (they won't redraw their backgrounds unless necessary now, for instance), because they tried to make it all happen magically, which is always always a bad idea.
Note that if you make an NSTextField the shadow code works as you'd expect. It's just NSButtons (so far) that I've found have this issue.
Please file a radar.

Layer-Backed view no longer punches hole in transparent NSWindow

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];

how to get window with semi-transparent blurred background

I'd like to get a window that has a semi-transparent blurred background, just like what the Terminal can do. See this video, about 30 sec in, to see what I mean: http://www.youtube.com/watch?v=zo8KPRY6-Mk
See an image here: http://osxdaily.com/wp-content/uploads/2011/04/mac-os-x-lion-terminal.jpg
I've been googling for an hour, and can't get anything to work. I believe I need to somehow create a core animation layer and add a background filter, but I've been unsuccessful so far... I just see the gray background of my window. Here's the code I've got so far:
Code:
// Get the content view -- everything but the titlebar.
NSView *theView = [[self window] contentView];
[theView setAlphaValue:0.5];
// Create core animation layer, with filter
CALayer *backgroundLayer = [CALayer layer];
[theView setWantsLayer:YES];
[theView setLayer:backgroundLayer];
CIFilter *blurFilter = [CIFilter filterWithName:#"CIGaussianBlur"];
[blurFilter setDefaults];
[theView layer].backgroundFilters = [NSArray arrayWithObject:blurFilter];
[[theView layer] setBackgroundFilters:[NSArray arrayWithObject:blurFilter]];
Any tips or examples to do what I'm trying to do?
Thanks!
no need for layers and filters, NSWindow can do it itself
[mywindow setOpaque:NO];
[mywindow setBackgroundColor: [NSColor colorWithCalibratedHue:0.0 saturation:0.0 brightness:0.2 alpha:0.5]];
please do not use this, as it will alpha your title bar also (post it here just in case others need)
[mywindow setOpaque:NO];
[mywindow setBackgroundColor: [NSColor blackColor]];
[mywindow setAlphaValue:0.5];
For the transparency use Jiulong Zhao's suggestion.
For a blurred background use this
The call on a NSWindow :
[self enableBlurForWindow:self];
The function :
-(void)enableBlurForWindow:(NSWindow *)window
{
//!!!! Uses private API - copied from http://blog.steventroughtonsmith.com/2008/03/using-core-image-filters-onunder.html
CGSConnection thisConnection;
uint32_t compositingFilter;
int compositingType = 1; // Under the window
/* Make a new connection to CoreGraphics */
CGSNewConnection(NULL, &thisConnection);
/* Create a CoreImage filter and set it up */
CGSNewCIFilterByName(thisConnection, (CFStringRef)#"CIGaussianBlur", &compositingFilter);
NSDictionary *options = [NSDictionary dictionaryWithObject:[NSNumber numberWithFloat:2.0] forKey:#"inputRadius"];
CGSSetCIFilterValuesFromDictionary(thisConnection, compositingFilter, (__bridge CFDictionaryRef)options);
/* Now apply the filter to the window */
CGSAddWindowFilter(thisConnection, [window windowNumber], compositingFilter, compositingType);
}
NB: It uses a private API
For those reading this in 2017 and using Swift 4 and wanting to change your BG Alpha you can add the following to your custom NSWindow class:
self.backgroundColor = NSColor.black
self.backgroundColor = NSColor.init(calibratedHue: 0, saturation: 0, brightness: 0, alpha: 0.2)
p.s. I do not need the blur effect yet and when I do, I'll update the answer

Custom NSView with NSTextField subview drawing issue

So I am drawing a custom window (transparent) with a custom NSView as the contentview, and wouldlike to add an NSTextField to the window as well, however, when I add the NSTextField, I get a weird resizing or redraw of the custom NSView, and I can't figure out what is causing the problem. Both the window and the contentview of the window are subclasses of NSWindow, and NSView, respectively. Also, I have tried to just layer the custom NSView (not set it as the contentview) with no change. Any ideas?
NSWindow *quickEntryWindow = [[TransparentWindow alloc] initWithContentRect:NSMakeRect([[NSScreen mainScreen] frame].size.width/2 - 250, [[NSScreen mainScreen] frame].size.height/2 + 50, 500, 100)
styleMask:NSBorderlessWindowMask
backing:NSBackingStoreBuffered
defer:NO
special:YES];
BorderView *quickEntryBorderView = [[BorderView alloc] initWithFrame:NSMakeRect(0, 0, [[quickEntryWindow contentView] frame].size.width, [[quickEntryWindow contentView] frame].size.height)];
[quickEntryBorderView canDrawConcurrently];
[quickEntryWindow setContentView:quickEntryBorderView];
NSTextField *quickEntryTextField = [[NSTextField alloc] initWithFrame:NSMakeRect(10, 10, [quickEntryBorderView frame].size.width-20, [quickEntryBorderView frame].size.height-20)];
[quickEntryTextField setAutoresizingMask:NSViewNotSizable];
[quickEntryTextField setBordered:NO];
[quickEntryTextField setDrawsBackground:NO];
[quickEntryTextField setFocusRingType:NSFocusRingTypeNone];
[quickEntryTextField setFont:[NSFont fontWithName:#"Helvetica" size:42]];
[quickEntryTextField setTextColor:[NSColor grayColor]];
[quickEntryBorderView addSubview:quickEntryTextField];
What I get is something that looks like this (it is normal when no text is entered into the NSTextField):
Oh yeah, I know I'm not managing my memory...I'm just trying to get this working. Thanks!

Xcode - UILabel is not changing value on viewDidAppear

Hey everyone, I have a iPhone App I am creating. It uses a uitableview and that goes to a detail view.
Now on the detail view I have a scroll view, and all the data (XML) comes into it fine, each time, no issues.
My UILabels in my scrollview do update with the correct data, but, they keep adding subviews on top of each other to keep adding label after label and it all looks mumbo jumbo.
I have tried everything to try and remove the subviews such as:
for(UIView *subview in [scrollView subviews]) {
[subview removeFromSuperview];
}
AND
[[scrollView subviews] makeObjectsPerformSelector:#selector(removeFromSuperview)];
So I am stumped, I got no idea what is happening. Any help? I have been searching Google for ages and agesm, but keep finding the same answers that don't work :(
Any direction to sites that might help that you know of will be greatly appreciated.
Thanks!
Here is what I have in my controller that loads all the scrollview and labels:
- (void)viewDidAppear:(BOOL)animated {
// Create the scroll view for this view
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:self.view.frame];
scrollView.contentSize = CGSizeMake(320, 270);
[scrollView setFrame:CGRectMake(0, 198, 320, 170)];
// Do the labels for the text
UILabel *dates = [[UILabel alloc] initWithFrame:scrollView.bounds];
dates.textColor = [UIColor whiteColor];
dates.font = [UIFont boldSystemFontOfSize:12];
dates.text = round_date;
dates.tag = 1;
[dates setBackgroundColor:[UIColor clearColor]];
[dates setFrame:CGRectMake(10, 10, 280, 22)];
[scrollView addSubview:dates];
[dates release];
// Add the content to the main scrollview
[self.view addSubview:scrollView];
[scrollView release];
}
Ok, the reason for this is that viewDidAppear will get called every time you present the view.
If you do this is
-(void) viewDidLoad;
instead.
To make sure you can access them later you might want to keep a reference to them in your UIViewController
-(void) viewDidLoad {
scrollView = [[UIScrollView alloc] initWithFrame];
[self.view addSubview:scrollView];
}
and define it in your header file with:
UIScrollView *scrollView;

Resources