Using Bezier Paths - objective-c

i defined this to convert degrees to radians
#define DEGREES_TO_RADIANS(degrees) ((3.14159 * degrees)/180)
then i added this code snippet to my viewDidLoad. I am trying to create an arc here.
UIBezierPath *innerPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(150, 150) radius:75 startAngle:0 endAngle:DEGREES_TO_RADIANS(135) clockwise:YES];
[[UIColor blackColor] setStroke];
[[UIColor redColor] setFill];
innerPath.lineWidth = 5;
[innerPath fill];
[innerPath stroke];
However the arc does not appear when I run my app in the simulator. Where am I going wrong?

First, use M_PI instead of 3.14159.
Second, you need to do your drawing in a view's drawRect: method. Read about the UIKit drawing model for more details.

Related

Custom NSView draws over controls on top of it

I have an NSView with a custom subclass that draws a grid of rounded rectangles inside it. This NSView was placed with interface builder and on top of it I have some NSButtons.
The problem is that sometimes when the view is re-drawn (ie, when i click a button on top of it) then it re-draws over some of the buttons that are meant to stay on top. When this happens only the smaller rounded rects appear over the buttons though, not the background one that is drawn before the loop.
Here is the code form drawRect:
[NSGraphicsContext saveGraphicsState];
NSBezierPath *path = [NSBezierPath bezierPathWithRect:self.bounds];
[[NSColor grayColor] set];
[path fill];
[NSGraphicsContext restoreGraphicsState];
for( int r = 0; r < 15; r++ ){
for( int c = 0; c < 15; c++ ) {
[NSGraphicsContext saveGraphicsState];
// Draw shape
NSRect rect = NSMakeRect(20 * c, 20 * r, 15, 15);
NSBezierPath *roundedRect = [NSBezierPath bezierPathWithRoundedRect: rect xRadius:1 yRadius:1];
[roundedRect setClip];
// Fill
[[NSColor colorWithCalibratedHue:0 saturation:0 brightness:0.3 alpha:1] set];
[roundedRect fill];
// Stroke
[[NSColor colorWithCalibratedHue:0 saturation:0 brightness:0.5 alpha:1] set];
[roundedRect setLineWidth:2.0];
[roundedRect stroke];
[NSGraphicsContext restoreGraphicsState];
}
}
Here's a screenshot:
Update: Simplified the code, added a screenshot.
the mac has issues with overlapping sibling views. it didn't work before.... 10.6 and it still doesnt work quite often.
use a proper superview / subview hierachy
OK I just managed to solve this by removing the setClip and finding a different way to draw the inner stroke.
I'm sure it's possible to solve this while still using setClip but this solution worked fine for me this time.

Core graphics - Seams show on diagonally joined sides

I'm having a problem in Core Graphics where two shapes that are supposed to fit together are showing seams between them when their sides are diagonal. This is causing problems. I can 'fix' this by overlapping the shapes. This works for simple stuff, but when the shape is more complex things start to fall apart.
Here is a simple example:
[[UIColor blackColor] setFill];
[path fill]; //fills the entire background black
UIBezierPath *path1 = [UIBezierPath bezierPath];
[path1 moveToPoint:CGPointMake(0.0f, 0.0f)];
[path1 addLineToPoint:CGPointMake(100, 0.0f)];
[path1 addLineToPoint:CGPointMake(50, rect.size.height)];
[path1 addLineToPoint:CGPointMake(0, rect.size.height)];
[path1 closePath];
UIBezierPath *path2 = [UIBezierPath bezierPath];
[path2 moveToPoint:CGPointMake(100, 0)];
[path2 addLineToPoint:CGPointMake(rect.size.width, 0)];
[path2 addLineToPoint:CGPointMake(rect.size.width, rect.size.height)];
[path2 addLineToPoint:CGPointMake(50, rect.size.height)];
[path2 closePath];
[[UIColor lightGrayColor] setFill];
[path1 fill];
[path2 fill];
This produces a seam that looks something like this:
This is exceptionally problematic because this is occurring in a CGPathApply call back function I'm using to process shapes (I divide the shapes up, do some processing and put them back together again). Because of this, it's very difficult to determine how those shapes should be shifted in such a way as to remove the seams (by overlapping) without distorting the shape. Especially since some of these shapes are rather complex (not simple rects).
I did discover that turning off antialiasing (CGContextSetShouldAntialias(context, NO);) does remove the seems but things just get ugly if I do that, so it's not really an option.
Any ideas?

Strange drawing behavior of NSSegmentedControl, when superview is drawn

I've got a problem with the NSSegmentedControl.
I have a custom view where I draw a gradient with a shadow.
The drawing is ok, but when I place a NSSegmentedControl inside it then something strange happens - the background of the custom view is shining through the segmented control, as if the segmented control had a low alpha value.
Here are pictures, that demonstrate the issue :
With the custom view in background :
Without the custom view in background :
As you can see in the left upper corner in the first image the segmented control isn't drawing properly.
Here is the drawRect method from the custom view :
- (void)drawRect:(NSRect)dirtyRect {
NSShadow *shadow = [[NSShadow alloc] init];
[shadow setShadowOffset:NSMakeSize(0.0, -6.0)];
[shadow setShadowBlurRadius:3.0];
[shadow setShadowColor:[[NSColor blackColor] colorWithAlphaComponent:0.3]];
[shadow set];
NSGradient *gradient = [[NSGradient alloc] initWithColorsAndLocations:[NSColor colorWithDeviceWhite:0.8 alpha:1.0],(CGFloat)0.0,[NSColor colorWithDeviceWhite:0.65 alpha:1.0],(CGFloat)0.5,[NSColor colorWithDeviceWhite:0.3 alpha:1.0],(CGFloat)0.5,[NSColor colorWithDeviceWhite:0.5 alpha:1.0],(CGFloat)1.0, nil];
[gradient drawInRect:self.bounds angle:90];
NSBezierPath *bezierPath = [NSBezierPath bezierPath];
[bezierPath moveToPoint:NSMakePoint(0, 0)];
[bezierPath lineToPoint:NSMakePoint(-self.bounds.size.height, 0)];
[bezierPath lineToPoint:NSMakePoint(-self.bounds.size.height, -self.bounds.size.width)];
[bezierPath lineToPoint:NSMakePoint(0, -self.bounds.size.width)];
[[NSColor whiteColor] set];
[bezierPath stroke];
}
Can you help me with this?
I don't know how to fix this issue.
I don't know if you're doing any custom drawing in the toolbar buttons, but I think your problem is that you're calling 'set' on the shadow without saving and restoring your graphics context.
Look at the documentation for NSShadow's set method.
NSShadow Documentation
"The shadow attributes of the receiver are used until another shadow is
set or until the graphics state is restored."
You should always save your graphics context first, then call any of Cocoa's set-style methods, then restore the context. This keeps future draw commands from applying your shadow.
NSGraphicsContext *context = [NSGraphicsContext currentContext];
[context saveGraphicsState];
NSShadow *myCoolShadow = [NSShadow new];
// Shadow code here
[myCoolShadow set];
[context restoreGraphicsState];
On 10.7+, Rounded Textured segmented controls and buttons are transparent. On 10.5-10.6, they are gradient filled.

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.

Redrawing NSCollectionView on Scroll Causes Breakup of Graphics

I have a minor irritant in an NSCollectionView in which the NSCollectionViewItem's break up visually when I scroll the window.
The rate of breakup depends upon the rate of scrolling. For example, if I scroll slowly the breakup occurs more often. Fast scrolling less so. It appears that the problem is when the custom NSView of the NSCollectionViewItem I am using crosses the boundary of the viewable frame.
My NSView (custom view of the NSCollectionViewItem) has a very simple drawing algorithm - nothing too complex.
Essentially I create a frame within the dirtyRect of the drawRect method and create a few frames inside that:
-(void)drawRect:(NSRect)dirtyRect
{
NSRect mOuterFrame = NSMakeRect(dirtyRect.origin.x, dirtyRect.origin.y, 104, 94);
NSRect mSelectedFrame = NSInsetRect(mOuterFrame, 2, 2);
NSRect mRedFrame = NSInsetRect(mSelectedFrame, 2, 2);
NSRect mInnerFrame = NSInsetRect(mRedFrame, 2, 2);
NSBezierPath * mOuterFramePath = [NSBezierPath bezierPathWithRect:mOuterFrame];
NSBezierPath * mSelectedFramePath = [NSBezierPath bezierPathWithRect:mSelectedFrame];
NSBezierPath * mRedFramePath = [NSBezierPath bezierPathWithRect:mRedFrame];
NSBezierPath * mInnerFramePath = [NSBezierPath bezierPathWithRect:mInnerFrame];
[mainBackgroundColor set];
[mOuterFramePath fill];
if (selected)
[[NSColor yellowColor] set];
else
[mainBackgroundColor set];
[mSelectedFramePath fill];
if (isRedBorder)
[[NSColor redColor] set];
else
[mainBackgroundColor set];
[mRedFramePath fill];
[[NSColor blackColor] set];
[mInnerFramePath fill];
}
I have tried locking the focus and releasing before and after the code as well as setting the graphics context and restoring it - none of which seem to solve the problem.
I am using Snow Leopard - not that I think that makes a difference.
Solution Update
For anyone interested here is the solution to the problem as recommended by NSResponder. I was creating the initial mOuterFrame based upon the drawRect: methods dirtyRect which, as pointed out, was the incorrect thing to do. A quick change from:
NSRect mOuterFrame = NSMakeRect(dirtyRect.origin.x, dirtyRect.origin.y, 104, 94);
To a 0 based origination point:
NSRect mOuterFrame = NSMakeRect(0, 0, 104, 94);
I also tweaked the efficiency of the code, being as I am using only rectangles, as suggested although the code change above was enough to resolve the problem in itself. I had to add a change to get a two pixel line. New method:
-(void)drawRect:(NSRect)dirtyRect
{
NSRect mOuterFrame = NSMakeRect(0, 0, 104, 94);
NSRect mSelectedFrame = NSInsetRect(mOuterFrame, 2, 2);
NSRect mRedFrame = NSInsetRect(mSelectedFrame, 2, 2);
NSRect mInnerFrame = NSInsetRect(mRedFrame, 2, 2);
[NSBezierPath setDefaultLineWidth:2.0];
[mainBackgroundColor set];
[NSBezierPath strokeRect:mOuterFrame];
if (selected)
[[NSColor yellowColor] set];
else
[mainBackgroundColor set];
[NSBezierPath strokeRect:mSelectedFrame];
if (isRedBorder)
[[NSColor redColor] set];
else
[mainBackgroundColor set];
[NSBezierPath strokeRect:mRedFrame];
[[NSColor blackColor] set];
[NSBezierPath strokeRect:mInnerFrame];
}
First thing I noticed is that your code above is rather wasteful, since you're creating a bunch of paths when you could just be using NSFrameRect() and NSRectFill() instead.
Secondly, the geometry of what you draw shouldn't be dependent on the rectangle passed to -drawRect:. That rectangle just tells you what the area to update is, so you can be more efficient in deciding what to draw if your view is complex.