Custom NSScrollers having weird effect from Core Animation - objective-c

I am making custom NSScrollers for use in NSScrollView. But when any of the superviews of the NSScroller gets Core Animation enabled. I get this drawing issue that's a huge nuisance. Whenever i scroll into the corners of the scroll view the scrollers clip. But if a scroller starts out drawing in the corner, the scroller part stays in that corner. What could be causing this problem? I have too much code to show on here. But here is my drawing code:
NSRect knobRect = [self rectForPart:NSScrollerKnob];
NSBezierPath *path = [NSBezierPath bezierPathWithRoundedRect:knobRect
xRadius:CornerRadius
yRadius:CornerRadius];
[path setLineWidth:2];
[[NSColor colorWithDeviceRed:0.114 green:0.114 blue:0.114 alpha:0.3] set];
[path fill];
[[NSColor colorWithDeviceWhite:1.0
alpha:0.100] set];
// Clip drawing to this path; draw nothing outwith the path.
[path addClip];
[path stroke];
I already tried to get rid of everything except the fill but it didnt work. This issue goes away if I call setNeedsDisplay:YES on the super view when setFloatValue is called.
Clipping Issue http://idkz.org/b814ee5c875c9f44cc2d1345e4489420

it seems that the answer to the problem seems to be to call:
[self setArrowsPosition:NSScrollerArrowsNone];
And it magically works :D
Edit, but I get a similar effect when I drag the scrollbar. :'( Is there anyway to clear the graphics without redrawing the scrollview itself? :(

You probably found this out by know, but in your case, the following piece should solve your drawing issue:
-(BOOL)isOpaque {
return NO;
}

Related

Problems with borderless window content disappearing

If I remove this method from my view everything works fine (no content disappears if I click on a button), so this is definitely the cause.
I'm trying to make a window that is rounded and has a gradient via the code below. Is there anything wrong with this at all that could cause content on the view to disappear?
- (void)drawRect:(NSRect)dirtyRect
{
[NSGraphicsContext saveGraphicsState];
NSBezierPath *outerClip = [NSBezierPath bezierPathWithRoundedRect:[self bounds]
xRadius:3.0
yRadius:3.0];
[outerClip setClip];
NSGradient* aGradient = [[NSGradient alloc]
initWithStartingColor:[NSColor colorWithCalibratedWhite:1.0 alpha:1.0]
endingColor:[NSColor colorWithCalibratedWhite:0.65 alpha:1.0]];
[aGradient drawInRect:[outerClip bounds] angle:270];
[NSGraphicsContext restoreGraphicsState];
}
Switching to NSGradient drawInbezierPath fixes the issue.
I know you answered your own question but I thought I would share why your content was vanishing with the above code.
When you use setClip you are removing the previous clipping path and replacing it with your new path. This means you will wind up drawing outside of the dirty area, thus overwriting previously drawn content.
I had the same issue with drawing rounded corners on my own splash screen, and eventually found a different way to do what I wanted.
Also, you can use the clipRect: class method of NSBezierPath to change the clipping path to the intersection of the existing path and the area you want to restrict drawing into. Of course, save and restore the graphics state around your call to clipRect:.

Fade effect at top and bottom of NSTableView/NSOutlineView

I'm looking for a way to draw a fade effect on a table view (and outline view, but I think it will be the same) when the content is scrolled. Here is an example from the Fantastical app:
Also a video of a similar fade on QuickLook windows here.
To make this I tried subclassing the scrollview of a tableview with this code:
#define kFadeEffectHeight 15
#implementation FadingScrollView
- (void)drawRect: (NSRect)dirtyRect
{
[super drawRect: dirtyRect];
NSGradient* g = [[NSGradient alloc] initWithStartingColor: [NSColor blackColor] endingColor: [NSColor clearColor]];
NSRect topRect = self.bounds;
topRect.origin.y = self.bounds.size.height - kFadeEffectHeight;
topRect.size.height = kFadeEffectHeight;
NSRect botRect = self.bounds;
botRect.size.height = kFadeEffectHeight;
[NSGraphicsContext saveGraphicsState];
[[NSGraphicsContext currentContext] setCompositingOperation: NSCompositeDestinationAtop];
// Tried every compositing operation and none worked. Please specify wich one I should use if you do it this way
[g drawInRect: topRect angle: 90];
[g drawInRect: botRect angle: 270];
[NSGraphicsContext restoreGraphicsState];
}
...but this didn't fade anything, probably because this is called before the actual table view is drawn. I have no idea on how to do this :(
By the way, both the tableview and the outlineview I want to have this effect are view-based, and the app is 10.7 only.
In Mac OS X (as your question is tagged), there are several gotchas that make this difficult. This especially true on Lion with elastic scrolling.
I've (just today) put together what I think is a better approach than working on the table or outline views directly: a custom NSScrollView subclass, which keeps two "fade views" tiled in the correct place atop its clip view. JLNFadingScrollView can be configured with the desired fade height and color and is free/open source on Github. Please respect the license and enjoy. :-)

Objective-C Drawing without drawRect: shapes won't display

I have a class called mapWindow which is hooked up to a window in IB.
No matter what, the red circle which I want the program to render won't show up unless the code is under drawRect or I move the window borders. Not even unlocking and locking the focus updates the window.
theOtherWindowView is actually a NSView hooked up to a custom view in IB.
- (void)test
{
[theOtherWindowView lockFocus];
NSBezierPath *path = [NSBezierPath bezierPath];
NSPoint center = [self drawPoint];
[path moveToPoint: center];
[path appendBezierPathWithArcWithCenter:center
radius:explosionRadius
startAngle:0
endAngle:360];
[[NSColor redColor] set];
[path fill];
[theOtherWindowView unlockFocus];
}
I don't want to use drawRect because I want multiple instances not one shape that has it's coordinates changed every update.
I've also tried [self lockFocus] and [mapWindow lockFous]
Keep doing your drawing in -drawRect:. When -drawRect: is sent, your view's coordinate system and clipping boundaries will have been set up for you, and your window's drawing context will be the current one.
In that method, draw as many of these circles as you want.

Stopping transparent drawing going all the way through NSViews

I'm doing some simple drawing in my NSView's drawRect with code like this
[[NSColor colorWithCalibratedWhite:0.250 alpha:0.700] set];
NSRectFill(dRect);
This drawing is being done in a nested view and I'm finding that the transparency goes all the way through the parent view and window showing some of the screen beneath it.
How do I get it so the transparency only goes through to the parent view (or any drawing that was done in that frame before it?
Thanks!
You really shouldn't be using NSRectFill to draw a transparent color. A call to
NSRectFill(rect);
is really just a shortcut to
NSRectFillUsingOperation(rect, NSCompositeCopy);
NSCompositeCopy doesn't perform compositing, so you can get unexpected results when a transparent NSColor is set.
Try using this instead:
[[NSColor colorWithCalibratedWhite:0.250 alpha:0.700] set];
[NSBezierPath fillRect:rect];
That should draw the transparency properly, with the underlying drawing visible.

Cocoa drawing glitches when window loses then regains main status

Various things, including (perhaps most notably) the window losing its main status and then being brought back into focus, cause some of my custom views to develop rendering glitches until I do something (such as resize the window) to cause these custom views to redraw. I assume this is due to some sort of caching bug in AppKit, and I'm sure I'm not the only one getting this behaviour, so does anyone know how to work around it?
Here's an example:
Notice the weird dark line at the left of the view's background? This is drawn even without the subviews in the view. I get other similar bugs in some of my other views.
Here's the code that does the drawing:
void EDDrawGlossEffectInRect(NSRect dirtyRect) {
NSRect topRect, bottomRect;
NSDivideRect(dirtyRect, &topRect, &bottomRect, (dirtyRect.size.height / 2), NSMaxYEdge);
[EDLightChromeColor set];
NSRectFill(topRect);
[EDMidChromeColor set];
NSRectFill(bottomRect);
}
and
-(void)drawRect:(NSRect)dirtyRect {
EDDrawGlossEffectInRect(dirtyRect);
NSBezierPath *path = [NSBezierPath bezierPath];
[path setLineWidth:1.0];
NSPoint startPoint = {0, dirtyRect.size.height};
NSPoint endPoint = {dirtyRect.size.width, dirtyRect.size.height};
[path moveToPoint:startPoint];
[path lineToPoint:endPoint];
[[NSColor colorWithCalibratedWhite:0.7 alpha:1] set];
[path stroke];
}
The view is created programatically, not with Interface Builder, which I'm not using for this project.
dirtyRect will not necessarily cover your view's visible rect, so you might want to see which part of your view's bounds intersect with the dirtyRect and just repaint that. Otherwise, just repaint the bounds of the view. Resizing the window can cause the entire bounds to be dirty (usually), which is probably why you're seeing full repaints at that point.