I have NSCollectionView and would like to set its background color, however using some of codes suggested here on stack I came to a problem - none of them fill the window entirely. You might be able to see a white (around 1px) white border around nscollection view except of the corners (collection view constraints are set to 0).
Ideally I would like to get rid of the white border.
Code for the first image:
- (void)drawRect:(NSRect)dirtyRect {
[super drawRect:dirtyRect];
CGContextRef context = (CGContextRef) [[NSGraphicsContext currentContext] graphicsPort];
CGContextSetRGBFillColor(context, 0.227,0.251,0.337,1);
CGContextFillRect(context, NSRectToCGRect(dirtyRect));
}
Code for the second image:
- (void)drawRect:(NSRect)dirtyRect {
[super drawRect:dirtyRect];
[[NSColor blueColor] setFill];
NSRectFill(dirtyRect);
}
Do you have Autolayout turned on? Any project warning? Go to MainMenu.xib and look in the right info panel. If Yes, then do something with the constraints.
Related
I want to set a background color to a view and as of now I have subclassed that view in Xib and in drawRect method I am setting the color.
- (void)drawRect:(NSRect)dirtyRect {
[[NSColor blackColor] setFill];
NSRectFill(dirtyRect);
[super drawRect:dirtyRect];
}
Am using [self.view enterFullScreenMode:screen withOptions:nil]; to show the view(which contains an image) in fullscreen. But when view is in fullscreen it shows a default gray background instead of black color. How can I set the background to black ?
Are you sure your methods calling? Put a NSLog to make sure.
If it is, then delete the line [super drawRect:dirtyRect];
Here is a NSView subclass that simply draws a subtle blue gradient (it's hard to see in this image but it is there) in it's bounds. Inside the view I place an NSTextField and set the cell to have NSBackgroundStyleRaised. The artefact is the background of the text field is draws with what looks like the same colour as it's superview. In this case the superview draws a gradient things look strange.
The -drawRect: for the blue view is,
- (void)drawRect:(NSRect)dirtyRect
{
[super drawRect:dirtyRect];
// Base colours
baseColor = [NSColor colorWithCalibratedRed: 0.271 green: 0.578 blue: 0.874 alpha: 1];
baseColorDarkened = [NSColor colorWithCalibratedRed: 0.159 green: 0.491 blue: 0.911 alpha: 1];
// Draw gradient
NSGradient* gradient = [[NSGradient alloc] initWithStartingColor:baseColor endingColor:baseColorDarkened];
NSBezierPath* rectanglePath = [NSBezierPath bezierPathWithRect:dirtyRect];
[gradient drawInBezierPath: rectanglePath angle: 90];
// Draw thin border
NSBezierPath *border = [NSBezierPath bezierPath];
[[baseColorDarkened blendedColorWithFraction:0.2 ofColor:[NSColor blackColor]] set];
[border setLineWidth:1];
[border stroke];
}
I'm using a document based application so in -windowControllerDidLoadNib: I set the properties of the text field,
- (void)windowControllerDidLoadNib:(NSWindowController *)aController
{
[super windowControllerDidLoadNib:aController];
[[_label1 cell] setBackgroundStyle:NSBackgroundStyleLowered];
[_label1 setBackgroundColor:[NSColor clearColor]]; // <-- Doesn't help :(
}
Some background styles seem to require special compositing (probably due to the shadows?).
OS X uses a cached & scaled representation of the superviews background when drawing the NSTextField.
I used your drawing code but changed the top color to make the cached drawing more apparent:
I found 2 workarounds:
Simply enable layer backing on the superview of the label.
When you can't enable layer backing, you could enforce a re-display of the label's superview. e.g. in the NSWindowDelegate method windowDidUpdate:
The second approach feels a bit hacky though:
- (void)windowDidUpdate:(NSNotification*)notification
{
[self.label.superview setNeedsDisplay:YES];
}
Result:
I wanted to create a focus ring outside a subclassed NSView to identify selection. My reference comes from here: Link.
I followed the reference, overwrote the -drawRect method as:
#property (nonatomic) BOOL shouldDisplayFocus;
...
- (void)drawRect:(NSRect)dirtyRect
{
// Drawing code here.
if (_shouldDisplayFocus)
{
[self setKeyboardFocusRingNeedsDisplayInRect:[self bounds]];
}
[super drawRect:dirtyRect];
[[NSColor blackColor] set];
NSRectFill(dirtyRect);
if (_shouldDisplayFocus)
{
NSSetFocusRingStyle(NSFocusRingTypeExterior);
NSBezierPath *path = [NSBezierPath bezierPathWithRect:NSInsetRect([self bounds], -1.0, -1.0)];
[[NSColor blackColor] set];
[path stroke];
[NSGraphicsContext restoreGraphicsState];
}
}
And its -mouseDown: method also overwritten:
- (void)mouseDown:(NSEvent *)theEvent
{
[super mouseDown:theEvent];
if (_delegate && [_delegate respondsToSelector:#selector(mouseDownAtView:withEvent:)])
{
[_delegate mouseDownAtView:self withEvent:theEvent];
}
}
And after the view is clicked, its delegate would set/un-set the focus ring and which would make its -drawRect: called again.
It worked and generated the focus ring outside the view correctly. However, one problem occurred soon:
I had an image view inside the subclassed view. As the image view rectangle was auto-layout with NSLayoutConstraint objects, I create four NSLayoutConstraint outlets to adjust their values. I do not frequently change the layout constraints. Actually, as the image size remained unchanged, I would not set them.
Here is the situation when the subclassed view not clicked (seemed fine):
Then click on the image (the focus ring generated, but...):
And I tried resize the window, things got even more sadly "FUNNY":
I could not understand why the problem is or how to solve that. Could anyone help me with that? I have uploaded my sample code here: Download
Quite sad that no one answer this question.
I noticed that the subviews also layouted incorrectly when they were add to this view by -addSubview: and -setFrame method.
Really late answer, but here it is anyway: you didn't call [NSGraphicsContext saveGraphicsState] at the start of the if (_shouldDisplayFocus) { block.
You call [NSGraphicsContext restoreGraphicsState] to pop the graphics state off the stack, but you never put anything on the stack. Cocoa is using the graphics state stack to draw everything so you are popping off some unknown state that has something to do with the position of the image. If you want to add the focus ring style and be able to remove the focus ring style you need to first save the graphics state, set the focus ring style to whatever you want, and then restore the graphics state back to what it was.
I'm trying to customize the UI of my application and I want my NSTableView to have rounded corners. So I subclassed NSTableView and got this:
However, when I populate the table and select a row, the selection is drawn over the border like this:
I've tried adding a clip in the table view drawing code and it doesn't work. Any suggestions for how I can fix this?
Edit:
My drawing code in the NSTableView is the following:
- (void)drawRect:(NSRect)dirtyRect {
[NSGraphicsContext saveGraphicsState];
NSRect frame = NSMakeRect(0.0, 0.0, [self bounds].size.width, [self bounds].size.height-1.0);
[[NSBezierPath bezierPathWithRoundedRect:frame xRadius:3.6 yRadius:3.6] addClip];
[super drawRect:dirtyRect];
[NSGraphicsContext restoreGraphicsState];
}
The actual rounded frame is drawn in the NSScrollView drawRect method. The interesting thing is that this does clip the selection of the very first and very last rows:
But not when the table is scrolling:
So the question remains: how can I clip all drawing inside the rounded frame of the NSScrollView?
I found that you can call this on the container scroll view of the table view.
self.scrollView.wantsLayer = TRUE;
self.scrollView.layer.cornerRadius = 6;
That's all I needed and it works. No subclassing needed.
I was able to solve this pretty nicely using CALayer. After trying subclassing everything from NSScrollView to NSTableView to NSClipView, and still getting the rendering problems shown above, I finally simply added this code to the drawRect of the NSScrollView subclass:
if (!self.contentView.wantsLayer) {
[self.contentView setWantsLayer:YES];
[self.contentView.layer setCornerRadius:4.0f];
}
And then I draw the frame in the same drawRect method of the NSScrollView. It solves all the problems above.
Im working on a drag n' drop view and found some handlers for drag and drop actions on the web. I want to make it so it turns blue when the user drags a file over the drag and drop area and gray again when they exit the drag and drop area. The issues is its not updating when you drag your mouse over it or exit it. Heres some of the code:
- (void)drawRect:(NSRect)rect
{
NSRect bounds = [self bounds];
[[NSColor grayColor] set];
[NSBezierPath fillRect:bounds];
}
- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender {
NSRect bounds = [self bounds];
[[NSColor blueColor] set];
[NSBezierPath fillRect:bounds];
return NSDragOperationCopy;
}
- (void)draggingExited:(id <NSDraggingInfo>)sender {
NSRect bounds = [self bounds];
[[NSColor grayColor] set];
[NSBezierPath fillRect:bounds];
}
Thanks for any help.
Are you calling [yourView: setNeedsDisplay] anywhere?
This is how you let the drawing framework know it needs to message your UIView subclass with drawRect:, so you should do it whenever things have changed. In your case, this probably means when the mouse enters or exits the drop area.
Drawing only works when a context (like a canvas for painting) is set up for you to draw into. When the framework calls -drawRect: it has set up a drawing context for you, so drawing commands like -[NSColor set] and -[NSBezierPath fillRect:] work as you expect.
Outside of -drawRect: there is usually no drawing context set up. Using drawing commands outside of -drawRect: is like waving a paintbrush in the air; there's no canvas, so no painting happens.
In 99.99% of cases, all view drawing should be kept within -drawRect: because NSView does a lot of work that you don't want to do to get the drawing context set up correctly and efficiently.
So, how do you change your view's drawing within your -draggingEntered: and -draggingExited: methods? By side effects.
You're doing the same thing in all three cases: 1) Setting a color and 2) Drawing a rectangle. The only difference is the color changes in each method. So, why not control which color you use in -drawRect: with an ivar, like so:
- (void)draggingEntered:(id <NSDraggingInfo>)sender {
drawBlueColorIvar = YES;
// ...
}
Then in -drawRect: you do this:
- (void)drawRect:(NSRect)rect {
NSColor *color = drawBlueColorIvar ? [NSColor blueColor] : [NSColor grayColor];
[color set];
[NSBezierPath fillRect:rect];
}
(Notice I didn't use [self bounds]. It is more efficient to just draw into the "dirty" rect, when possible.)
Finally, you need some way to tell the framework that your view needs to redraw when drawBlueColorIvar changes. The framework won't draw anything unless it's told it needs to. As Chris Cooper said, you do this with [self setNeedsDisplay:YES]. This should go after any place you change drawBlueColorIvar.