Set the border of a subclassed NSTableCellView - objective-c

As mentioned in the title of this post I want to set the border (color and width) of a subclassed NSTableCellView which is used inside a view-based NSTableView. I tried the following
self.layer.borderColor = [[NSColor greenColor] CGColor];
self.layer.borderWidth = 3.0;
I placed the code in initWithCoder and awakeFromNib without the desired result. Changing the backgroundColor is possible within the drawRect-Method. Can someone point me to the right direction?
Thanks
EDIT
Here is my solution using NSFrameRect
- (void)drawRect:(NSRect)dirtyRect
{
[NSGraphicsContext saveGraphicsState];
[[NSColor lightGrayColor]set];
NSFrameRect([self bounds]);
[NSGraphicsContext restoreGraphicsState];
}

Views on OS X are not layer backed by default. You first need to setWantsLayer: YES
But if you are using drawRect: you can just use NSFrameRect() or one of the similar functions or draw with an NSBezierPath in you cell view subclass. However, keep in mind that usually the row view does background drawing in view based tables.
Sounds like you have a bit of learning yet to do about drawing in Cocoa.

Related

Subviews of NSRect not affected by NSAffineTransform

When applying an NSAffineTransform rotate and translate to an NSRect, none of the sub views are affected by the transform. I've included the code below.
- (void)drawRect:(NSRect)dirtyRect
{
NSAffineTransform * transform = [NSAffineTransform transform];
[transform translateXBy:13.0 yBy:3.0];
[transform rotateByDegrees:-45];
[transform translateXBy:-13.0 yBy:3.0];
[transform concat];
NSImageView *imageViewWatchPointer;
NSRect watchPointer = NSMakeRect(0, 0, 134, 49);
imageViewWatchPointer = [[NSImageView alloc] initWithFrame:watchPointer];
[imageViewWatchPointer setImageScaling:NSScaleNone];
[imageViewWatchPointer setImage:[NSImage imageNamed:#"mastery_watch_pointer"]];
[self addSubview:imageViewWatchPointer];
[[NSColor blueColor] set];
NSRectFill(watchPointer);
}
You're not applying transform to anything. You're creating it and then throwing it away. In any case, you never add subviews in drawRect:. drawRect: is for custom-drawing the current view, not for managing the view hierarchy. The way you've done this, you're adding a new NSImageView every time the main view is redrawn.
I recommend going back to the Cocoa Drawing Guide to understand how Cocoa drawing works.
Looking at your code, I don't believe you need drawRect: at all. You just want to add a subview, probably in awakeFromNib or initWithFrame:. I assume you want the view rotated. The easiest way to do that is to give it a backing layer (setWantsLayer:), and then set the affineTransform on the layer.
Alternately, in drawRect:, you could rotate the context and just draw the image itself (without using an NSImageView). See the Cocoa Drawing Guide for details on how to do custom drawing.

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. :-)

Issue with setPatternPhase: in an NSSplitView for NSColor using colorWithPatternImage:

I have searched around and haven't found anything regarding this particular problem. I have a window with a vertical NSSplitView. Within the right view of the splitview, I have an NSBox object set to fill the contents of the view. I am loading in custom nibs using setContentView: to swap in views to the NSBox on the righthand side of the splitview. In my custom views I'm tying to fill the views with an NSColor object that uses a NSImage to pattern the background. All of this is working as expected. I am using the code based on this article: http://www.mere-mortal-software.com/blog/details.php?d=2007-01-08 to set the phase of the pattern to the top-left corner:
[[NSGraphicsContext currentContext] saveGraphicsState];
[[NSGraphicsContext currentContext] setPatternPhase: NSMakePoint( [self bounds].origin.x,
[self bounds].size.height )];
[[self leatherColor] set];
NSRectFill( [self bounds] );
[[NSGraphicsContext currentContext] restoreGraphicsState];
with the intention that the pattern appears to remain constant in the upper lefthand corner of the NSBox when the views are re-sized as would be the expected behavior (via the splitview moving or the window itself being resized). I have implemented this in a stripped-down test app with just a window and a custom view and it behaves as expected.
In my project with the splitview however, it is behaving like the patternPhase is being set on the upper lefthand corner of the entire window instead of the upper lefthand corner of the right-half of the splitview (i.e. it should shift with resizing the splitview) It behaves like it's "revealing a larger image instead of shifting with it. I'm pretty certain this is an issue with multiple coordinate spaces, me misunderstanding how the coordinates of an NSSplitView work, or something to do with the view hierarchy. From the documentation on NSGraphicsContext, in the setPatternPhase: method it says: "For example, setting the pattern phase to (2,3) has the effect of moving the start of pattern cell tiling to the point (2,3) in default user space." Will the default user space correspond with the bounds of my custom view, or could it be that I am creating the point based on the right side view set of coordinates and the setPatternPhase implements it for the full width of the spitview (including the left-half)?
Any suggestions/advice/help you could provide would be greatly appreciated.
Edit: I have uploaded a copy of the project so you can see the behavior: http://dl.dropbox.com/u/6679821/ViewSwitcher.zip
You need to adjust the phase depending on where the view sits in the window. I use this category on NSView:
#implementation NSView (RKAdditions)
- (void)rk_drawPatternImage:(NSColor*)patternColor inRect:(NSRect)rect
{
[self rk_drawPatternImage:patternColor inBezierPath:[NSBezierPath bezierPathWithRect:rect]];
}
- (void)rk_drawPatternImage:(NSColor*)patternColor inBezierPath:(NSBezierPath*)path
{
[NSGraphicsContext saveGraphicsState];
CGFloat yOffset = NSMaxY([self convertRect:self.bounds toView:nil]);
CGFloat xOffset = NSMinX([self convertRect:self.bounds toView:nil]);
[[NSGraphicsContext currentContext] setPatternPhase:NSMakePoint(xOffset, yOffset)];
[patternColor set];
[path fill];
[NSGraphicsContext restoreGraphicsState];
}
#end
In your case you'd modify -drawRect: to look like this:
-(void) drawRect: (NSRect)dirtyRect
{
[self rk_drawPatternImage:[self leatherColor] inRect:self.bounds];
}

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.

Custom NSScrollers having weird effect from Core Animation

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