How to make NSView's background image not repeat? - objective-c

I want replicate the effect obtained by the following CSS code:
background: white url(./img/background.png) no-repeat;
I've written a subclass of NSView and override drawRect in this way:
- (void)drawRect:(NSRect)dirtyRect
{
dirtyRect = [self bounds];
[[NSColor whiteColor] setFill];
NSRectFill(dirtyRect);
[[NSColor colorWithPatternImage:[NSImage imageNamed:#"background.png"]] setFill];
NSRectFill(dirtyRect);
}
(I apologize for my bad english)

Take a look at NSImage class reference. Image can be drawn with drawInRect:fromRect:operation:fraction: and also with drawAtPoint:fromRect:operation:fraction:.
So You can Use this:
[[NSImage imageNamed:#"background.png"] drawInRect:dirtyRect fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1]; // Passing NSZeroRect causes the entire image to draw.
Instead this:
[[NSColor colorWithPatternImage:[NSImage imageNamed:#"background.png"]] setFill];
NSRectFill(dirtyRect);

Just use NSImage drawInRect:fromRect:operation:fraction: to draw your image in the view instead of filling your rect with a pattern color.

Related

How to draw background image repeatedly with Mac OS

I am new in Mac OS programming. I would like to draw image repeatedly in background since my original image is small. Here is the code of drawing the image, but it seems enlarge the image which means it only draw one image instead of multiple.
// overwrite drawRect method of NSView
-(void)drawRect:(NSRect)dirtyRect{
[[NSImage imageNamed:#"imageName.png"] drawInRect:dirtyRect fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1];
[super drawRect:dirtyRect];
}
This should work for you...
// overwrite drawRect method of NSView
-(void)drawRect:(NSRect)dirtyRect{
[super drawRect:dirtyRect];
// Develop color using image pattern
NSColor *backgroundColor = [NSColor colorWithPatternImage:[NSImage imageNamed:#"imageName.png"]];
// Get the current context and save the graphic state to restore it once done.
NSGraphicsContext* theContext = [NSGraphicsContext currentContext];
[theContext saveGraphicsState];
// To ensure that pattern image doesn't truncate from top of the view.
[[NSGraphicsContext currentContext] setPatternPhase:NSMakePoint(0,self.bounds.size.height)];
// Set the color in context and fill it.
[backgroundColor set];
NSRectFill(self.bounds);
[theContext restoreGraphicsState];
}
Note: You may like to consider creating backgroundColor as part of the object for optimization as drawRect is called pretty often.

Rounded NSView in a Transparent Window

I'm trying to make a transparent NSWindow with a rounded view in there.
I'm trying to have a rounded view with a transparent window.
This is what it looks like now: (see the little dots in the corners)
Here's another example with the border radius set to 10px (set in NSView drawRect):
I am using code from this Apple sample: https://developer.apple.com/library/mac/#samplecode/RoundTransparentWindow/Introduction/Intro.html
Specifically this method in my NSWindow subclass:
- (id)initWithContentRect:(NSRect)contentRect
styleMask:(NSUInteger)aStyle
backing:(NSBackingStoreType)bufferingType
defer:(BOOL)flag {
// Using NSBorderlessWindowMask results in a window without a title bar.
self = [super initWithContentRect:contentRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO];
if (self != nil) {
// Start with no transparency for all drawing into the window
[self setAlphaValue:1.0];
// Turn off opacity so that the parts of the window that are not drawn into are transparent.
[self setOpaque:NO];
[self setBackgroundColor:[NSColor clearColor]];
}
return self;
}
And this in my NSView subclass:
- (void)drawRect:(NSRect)dirtyRect
{
[[NSColor redColor] set];
NSBezierPath* thePath = [NSBezierPath bezierPath];
[thePath appendBezierPathWithRoundedRect:dirtyRect xRadius:3 yRadius:3];
[thePath fill];
}
Can anyone tell me what I'm missing here?
Thanks.
Are you looking for something like the following, where there's a red outline (stroke), but the center area is transparent?
If so, to achieve that, I used the following code:
- (void)drawRect:(NSRect)frame {
frame = NSInsetRect(self.frame, 3.0, 3.0);
[NSBezierPath setDefaultLineWidth:6.0];
NSBezierPath *path = [NSBezierPath bezierPathWithRoundedRect:frame
xRadius:6.0 yRadius:6.0];
[[NSColor redColor] set];
[path stroke];
}
If that's what you're looking for, you can probably use that as a starting point. You'll want to make sure that you inset the frame rect one half of the stroke line width, so as to avoid the problem with clipping the corners like you were seeing.
Not sure if this is what you are looking for but there is a great class by Matt Gemmell called MAAttachedWindow and can be found here: http://mattgemmell.com/2007/10/03/maattachedwindow-nswindow-subclass/
It's a little older but still works great for me when I need to do a 'floating' popup window and configure transparency, border radii, and even add a small arrow for context if desired. I use it all the time.

Proper place to put NSTextField drawing code?

I am working on creating some custom Cocoa components. Currently I'm trying to figure out how to draw custom NSTextFields.
I have overridden the drawRect method on my subclass but when i start typing, i get a double rectangle like this http://imgur.com/a/LpUMy.
Here is my drawRect method
- (void)drawRect:(NSRect)dirtyRect
{
[NSGraphicsContext saveGraphicsState];
[[NSBezierPath bezierPathWithRoundedRect:dirtyRect xRadius:5.0f yRadius:5.0f] setClip];
[[NSColor grayColor] setFill];
NSRectFillUsingOperation(dirtyRect, NSCompositeSourceOver);
[NSGraphicsContext restoreGraphicsState];
[NSGraphicsContext saveGraphicsState];
NSRect rect = NSInsetRect(dirtyRect, 1.0f, 1.0f);
[[NSBezierPath bezierPathWithRoundedRect:rect xRadius:5.0f yRadius:5.0f] setClip];
[[NSColor whiteColor] setFill];
NSRectFillUsingOperation(rect, NSCompositeSourceOver);
[NSGraphicsContext restoreGraphicsState];
}
UPDATE:
I moved my drawing code into a NSTextFieldCell subclass as so
- (void)drawWithFrame:(NSRect)frame inView:(NSView *)controlView {
[NSGraphicsContext saveGraphicsState];
[[NSBezierPath bezierPathWithRoundedRect:frame xRadius:5.0f yRadius:5.0f] setClip];
[[NSColor grayColor] setFill];
NSRectFillUsingOperation(frame, NSCompositeSourceOver);
[NSGraphicsContext restoreGraphicsState];
[NSGraphicsContext saveGraphicsState];
[[NSBezierPath bezierPathWithRoundedRect:NSInsetRect(frame, 1.0f, 1.0f) xRadius:3.0f yRadius:3.0f] setClip];
[[NSColor whiteColor] setFill];
NSRectFillUsingOperation(frame, NSCompositeSourceOver);
[NSGraphicsContext restoreGraphicsState];
}
But as soon as you are done editing it draws over the text, even though the cursor is still there? Any suggestions? I've tried drawing the title but it still happens.
Thanks for your help.
Answer:
NSCell Custom Highlight
By calling super drawInteriorWithFrame:inView I was able to stop the weird text disappearing issues.
It looks to me like you've ended up drawing inside the drawing of your superclass's (NSTextField's) drawRect: implementation. You haven't called super but it still manages to draw itself. I'm not sure why myself, but some NSControls such as text fields and buttons, when subclassed, will draw themselves regardless of whether or not you call drawRect: on them. For example, if you subclass a plain NSButton, implement drawRect: and don't call super, it'll draw the button anyways. Potentially over whatever you drew, which has caused confusion in the past. The easiest solution is to not subclass NSTextField, and see if there's another class you can subclass (like NSTextFieldCell mentioned in the comment).

NSWindow Shadow Outline

I am drawing a custom window by setting a custom content view for the window. When I draw the custom view I give it rounded corners and a nice outline to mimic a proper window.
However, I see another 1 px outline around the window which strays from the edge at the corners. I have found that if I turn off the shadow it goes away, but obviously as this wants to act like a window I need the shadow. Here's what I mean about the 1px outline:
How can I prevent this?
EDIT
Code for drawing the custom window's content view:
NSBezierPath *path = [NSBezierPath bezierPathWithRoundedRect:[self bounds] cornerRadius:5];
NSGradient* aGradient = [[[NSGradient alloc] initWithColorsAndLocations:
[NSColor colorWithDeviceRed:0.5569 green:0.5137 blue:0.4588 alpha:1.0000], 0.0,
[NSColor colorWithDeviceRed:0.5569 green:0.5137 blue:0.4588 alpha:1.0000], 1.0,
nil] autorelease];
[aGradient drawInBezierPath:path angle:90];
[path setLineWidth:4];
[[NSColor colorWithDeviceRed:0.4235 green:0.3922 blue:0.3451 alpha:0.9000] setStroke];
[path strokeInside];
[path setLineWidth:3];
[[NSColor colorWithDeviceRed:0.8431 green:0.8314 blue:0.8078 alpha:1.0000] setStroke];
[path strokeInside];
[path setLineWidth:1];
[[NSColor colorWithDeviceRed:0.4235 green:0.3922 blue:0.3451 alpha:0.9000] setStroke];
[path strokeInside];
Don't ask me how I got this, but this will solve your problem.
Define a category for NSWindow with the following content:
#implementation NSWindow(NoShadowRim)
- (id)_shadowRimInfo {
return #{
#"kCUIMeasureWindowFrameRimDensity": [NSNumber numberWithInt:0]
};
}
#end
DISCLAIMER: This overrides the internal method of NSWindow, so use it at your own risk. It may break with any OS X update.
You need to tell the window to recompute its shadow by sending it -invalidateShadow.
Try:
[[self window] display];
[[self window] setHasShadow:NO];
[[self window] setHasShadow:YES];
This line contouring the window area is drawn automatically. I have a window which has this line running accurately around bottom rounded corners. You have to setup the window as non-opaque and the background color to transparent:
[self setOpaque:NO];
[self setBackgroundColor:[NSColor clearColor]];
The somewhere in the contentView -drawRect: you do
[NSGraphicsContext saveGraphicsState];
[pathWithBottomRoundedCorner addClip];
// your drawing here...
[NSGraphicsContext restoreGraphicsState];
That should work.
As I understand correctly, shadows are drawn by windows server. When you draw custom NSWindow with rounded corners or other not rectangular shapes, window server don't count those transparent pixels and dont drop shadow under them.
I developed some hack to avoid such behavior. Just drop additional shadow under your path, something like this:
NSShadow *headShadow = [[NSShadow alloc] init];
[headShadow setShadowColor:[NSColor colorWithSRGBRed:0.0
green:0.0
blue:0.0
alpha:0.16]];
[headShadow setShadowBlurRadius:0.0f];
[headShadow setShadowOffset:NSMakeSize(0.0f, 0.0f)];
[headShadow set];
Ideally for perfect result i fink shadow must be equal to window servers.

How to draw over a subview of NSView

I'm trying to draw at the top of my NSView which has some subviews.
In fact I'm trying to reproduce the connection line style of Interface Builder. Here is the code I'm using for the moment:
- (void)drawRect:(CGRect)dirtyRect
{
// Background color
[[NSColor whiteColor] setFill];
NSRectFill(dirtyRect);
// Draw line
if(_connecting)
{
CGContextRef c = [[NSGraphicsContext currentContext] graphicsPort];
[[NSColor redColor] setStroke];
CGContextMoveToPoint(c, _start.x, _start.y);
CGContextAddLineToPoint(c, _end.x, _end.y);
CGContextSetLineWidth(c, LINE_WIDTH);
CGContextClosePath(c);
CGContextStrokePath(c);
}
}
The first part is to color my NSView (if you know an other way, tell me please 'cause I come from iPhone development and I miss the backgroundColor property of UIView)
Then if a connection if detected, I draw it with 2 NSPoints. This code works but I didn't get it to draw over subviews, only on the first NSView.
A parent view cannot draw over its subviews. You would have to place another view over the subviews and draw the line there.