Reputation: 6566
I have a window with text field and bezel button. It's borderless and transparent, but the issue reproduces on any window.
The content view of that window is set in IB to a custom class that draws window's background.
Here's the code:
- (void)drawRect:(NSRect)dirtyRect
{
[NSGraphicsContext saveGraphicsState];
float cornerRadius = 10;
NSBezierPath *path = [NSBezierPath bezierPathWithRoundedRect:self.bounds xRadius:cornerRadius yRadius:cornerRadius];
[path setClip];
NSGradient *gradient = [[NSGradient alloc] initWithColorsAndLocations:
[NSColor colorWithCalibratedRed:0.96f green:0.96f blue:0.96f alpha:1.00f], 0.0f,
[NSColor colorWithCalibratedRed:0.84f green:0.84f blue:0.84f alpha:1.00f], 1.0f,
nil];
[gradient drawInRect:self.bounds angle:270];
[NSGraphicsContext restoreGraphicsState];
}
It causes some really weird artifacts, like disappearing objects or the text field's background changing to window's:
What's going on? I've tried to isolate it, I've been playing around with this "graphics context state saving" thing (which I'm not sure if I understand correctly), but the problem persists.
I have XCode 4.4, SDK is 10.7 (so is my OS), and the deployment target is 10.6. It probably doesn't matter, but I've been doing something similar in the past and I've never had such strange issues.
Upvotes: 5
Views: 1765
Reputation: 16473
I agree this is weird / annoying. One would think that it's just a matter of where the layers may be interfering with one another.. but it seems difficult to solve without doing the following..
Here is the view as you describe, messed up. and after - with a simple checkbox changed via interface builder..
I have found that by enclosing the troubled controls in a box or a view or whatnot and then clicking on the "Core animation" "setWantsLayer" button in interface builder will allow those interface elements to shine through without all the kurfuffle.
Kind of a dumb workaround… but in reality it works - and is easy.
PS - I find that it helps to make parameters a little bit more dramatic (colors, etc) when troubleshooting this kind of thing - especially if you're trying to describe to people what's wrong..
Upvotes: 0
Reputation: 181
The problem seems to be the [path setClip] method. I commented this out and the text field and button drew properly.
So, I then replaced the line:
[gradient drawInRect:self.bounds angle:270];
with
[gradient drawInBezierPath:path angle:270];
and everything worked perfectly. The button and text field appeared as expected.
The Class Reference for NSBezierPath states:
You should avoid using this method as a way of adjusting the clipping path, as it may expand the clipping path beyond the bounds set by the enclosing view. If you do use this method, be sure to save the graphics state prior to modifying the clipping path and restore the graphics state when you are done.
This method uses the current winding rule to determine the clipping shape of the receiver. This method does not affect the receiver’s path.
Does not really explain the problems you saw, but lead me to see how else I could draw the gradient.
Upvotes: 6
Reputation: 6566
One workaround to make it work is to make (at window's -awakeFromNib or -init) NSImage with the same size as self.bounds and then draw this gradient into the image (use image's -lockFocus, -unlockFocus). You can then set the image as background using window.backgroundColor = [NSColor colorWithPatternImage:image];
It works, but I'm not happy with the solution.
I don't see any reason why above wouldn't work. And I've had similar odd problems with drawing on views, so I want to know the real reason why it didn't work.
Upvotes: 0