I just want to draw a rect on a view. This is my code in my UIView subclass:
- (void)drawRect:(CGRect)rect
{
context = UIGraphicsGetCurrentContext();
CGContextSetRGBFillColor(context, 255.0/255.0, 0.0/255.0, 0.0/255.0, 1);
CGContextAddRect(context, (CGRectMake(20, 20, 20, 20)));
}
When I run it no rect is drawn. What's wrong?
CGContextAddRect just adds a rectangular path to the context. You need to stroke or fill the path as well using CGContextFillPath or CGContextStrokePath.
You can also fill a rect directly with UIRectFill or CGContextFillRect.
Related
I followed another StackOverflow post that explains how i could override the draw method of a PDFAnnotation so i could draw a picture instead of a traditional PDFAnnotation.
But sadly i was not able to achieve that and the annotation that is drawn on top of my pdf is still a regular one.
This is the code that i used :
#implementation PDFImageAnnotation { UIImage * _picture;
CGRect _bounds;};
-(instancetype)initWithPicture:(nonnull UIImage *)picture bounds:(CGRect) bounds{
self = [super initWithBounds:bounds
forType:PDFAnnotationSubtypeWidget
withProperties:nil];
if(self){
_picture = picture;
_bounds = bounds;
}
return self;
}
- (void)drawWithBox:(PDFDisplayBox) box
inContext:(CGContextRef)context {
[super drawWithBox:box inContext:context];
[_picture drawInRect:_bounds];
CGContextRestoreGState(context);
UIGraphicsPushContext(context);
};
#end
Does someone know how i could override the draw method so i could draw a custom Annotation ?
Thank You !
ps: i also tried to followed the tutorial on the apple dev site.
UPDATE :
Now i'm able to draw pictures using CGContextDrawImage but i'm not able to flip coordinates back in place. when i do that mi pictures are not drawn and it seems that they are put outside of the page but i'm not sure.
This is my new code :
- (void)drawWithBox:(PDFDisplayBox) box
inContext:(CGContextRef)context {
[super drawWithBox:box inContext:context];
UIGraphicsPushContext(context);
CGContextSaveGState(context);
CGContextTranslateCTM(context, 0.0, _pdfView.bounds.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextDrawImage(context, _bounds, _picture.CGImage);
CGContextRestoreGState(context);
UIGraphicsPopContext();
}
I also tried to follow the tutorial on the Apple dev site.
Which one?
Custom Graphics
Adding Custom Graphics to a PDF
Because both include UIGraphicsPushContext(context) & CGContextSaveGState(context) calls, but your code doesn't. Do not blindly copy & paste examples, try to understand them. Read what these two calls do.
Fixed code:
- (void)drawWithBox:(PDFDisplayBox) box
inContext:(CGContextRef)context {
[super drawWithBox:box inContext:context];
UIGraphicsPushContext(context);
CGContextSaveGState(context);
[_picture drawInRect:_bounds];
CGContextRestoreGState(context);
UIGraphicsPopContext();
}
The image was drawn with CGRectMake(20, 20, 100, 100). It's upside down, because PDFPage coordinates are flipped (0, 0 = bottom/left). Leaving it as an exercise for OP.
Rotation
Your rotation code is wrong:
CGContextTranslateCTM(context, 0.0, _pdfView.bounds.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextDrawImage(context, _bounds, _picture.CGImage);
It's based on _pdfView bounds, but it should be based on the image bounds (_bounds). Here's the correct one:
- (void)drawWithBox:(PDFDisplayBox) box
inContext:(CGContextRef)context {
[super drawWithBox:box inContext:context];
UIGraphicsPushContext(context);
CGContextSaveGState(context);
CGContextTranslateCTM(context, _bounds.origin.x, _bounds.origin.y + _bounds.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
[_picture drawInRect:CGRectMake(0, 0, _bounds.size.width, _bounds.size.height)];
CGContextRestoreGState(context);
UIGraphicsPopContext();
}
I used following code to draw line which works fine but How to move that drawn line within context by touch event?
- (void) drawLineFrom:(CGPoint)from to:(CGPoint)to width:(CGFloat)width
{
UIGraphicsBeginImageContext(self.frame.size);
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextScaleCTM(ctx, 1.0f, -1.0f);
CGContextTranslateCTM(ctx, 0.0f, -self.frame.size.height);
if (drawImage != nil) {
CGRect rect = CGRectMake(0.0f, 0.0f, self.frame.size.width, self.frame.size.height);
CGContextDrawImage(ctx, rect, drawImage.CGImage);
}
CGContextSetLineCap(ctx, kCGLineCapRound);
CGContextSetLineWidth(ctx, width);
CGContextSetStrokeColorWithColor(ctx, self.drawColor.CGColor);
CGContextMoveToPoint(ctx, from.x, from.y);
CGContextAddLineToPoint(ctx, to.x, to.y);
CGContextStrokePath(ctx);
CGContextFlush(ctx);
drawImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
drawLayer.contents = (id)drawImage.CGImage;
}
How to get reference of drawn line from CGContext ? thanks in advance.
UIGraphicsBeginImageContext() begins an image context — one that is a grid of pixels.
drawImage = UIGraphicsGetImageFromCurrentImageContext(); and drawLayer.contents = (id)drawImage.CGImage; set the contents of a layer to a captured form of that grid of pixels.
The intermediate UIGraphicsEndImageContext() ends the context you had. The context no longer exists.
So to answer you question literally:
you can't ask an image context to tell you what was drawn to it and expect it to have any higher-level insight than the individual pixels that were plotted;
you can't ask the context you drew to anything, because it no longer exists.
The normal thing to do would be to create a UIView with a bunch of properties that describe whatever you want about the line. Implement -drawRect: and in there draw the line based on the properties. When you want to update the line, update the properties. Ensure the property setters make a call to setNeedsDisplay.
In UIKit things that are interactive should be subclasses of UIView. Views draw when requested to by the system. The normal pattern is pull, not push.
Here is a picture of two lines drawn in a UITableViewCell with the same function, and same width, and color
As you can see the bottom line is a lot thicker than the other line.
The code I am using for drawing:
[CSDrawing drawLineWithColor:[UIColor blackColor] width:1.0 yPosition:1.0 rect:rect];
[CSDrawing drawLineWithColor:[UIColor blackColor] width:1.0 yPosition:CGRectGetMaxY(rect) - 3.0 rect:rect]; // draw a line on top and bottom
+(void)drawLineWithColor:(UIColor *)color width:(CGFloat)width yPosition:(CGFloat)yPosition rect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGContextMoveToPoint(context, 0.0, yPosition);
CGContextAddLineToPoint(context, CGRectGetMaxX(rect), yPosition);
CGContextSetStrokeColorWithColor(context, color.CGColor);
CGContextSetLineWidth(context, width);
CGContextStrokePath(context);
CGContextRestoreGState(context);
}
The problem was with the backgroundView being stretched to fit the content of the cell when the cell was being reused. When the cell was bigger the pixels were stretched. This is solved be setting the contentMode property to UIViewContentModeRedraw
I want to implement freeform drawing in my app. First, I tried the code inside drawLayer:inContext: and it gave me the result I wanted.
Drawing in CALayer:
But when I decided to implement the code inside drawRect:, this happened:
Even if I draw inside the white space, the drawing is rendered outside as shown above. The code I used is exactly the same. I copy-pasted it from drawLayer:inContext: to drawRect:. I didn't change a thing, so why is this happening?
The Code:
CGContextSaveGState(ctx);
CGContextSetLineCap(ctx, kCGLineCapRound);
CGContextSetLineWidth(ctx, 1.0);
CGContextSetRGBStrokeColor(ctx, 1, 0, 0, 1);
CGContextBeginPath(ctx);
CGContextMoveToPoint(ctx, prevPoint.x, prevPoint.y);
for (NSValue *r in drawnPoints){
CGPoint pt = [r CGPointValue];
CGContextAddLineToPoint(ctx, pt.x, pt.y);
}
CGContextStrokePath(ctx);
CGContextRestoreGState(ctx);
I see you are using app in full screen mode where the view is centered and does not take full width of the screen.
It may be that CALayer has transform applied to it that translates the drawing from the left side of the screen to the center. This may not be the case with drawRect:. Try setting CGContext's transform matrix:
CGContextSaveGState(ctx);
CGFloat xOffset = CGRectGetMidX(screenFrame) - CGRectGetMidX(viewFrame);
CGContextTranslateCTM(ctx, xOffset, 0.0f);
// rest of drawing code
// ...
CGContextRestoreGState(ctx);
Can anyone guide me in the correct way to build a colored bubble/circle programmatically?
I can't use images as I need it to be able to be any color depending on user interaction.
My thought was maybe to make a white circle image and then overlay a color on top of it.
However I am not sure if this would work, or how to really go about it.
If someone could point me the right direction I would appreciate it.
There are a couple steps to drawing something in Cocoa.
First you need a path that will be used to define the object that you are going to be drawing. Take a look here Drawing Fundamental Shapes for a guide on creating paths in Cocoa. You will be most interested in sending the "appendBezierPathWithOvalInRect" message to an "NSBezierPath" object, this takes a rectangle that bounds the circle you want to draw.
This code will create a 10x10 circle at coordinates 10,10:
NSRect rect = NSMakeRect(10, 10, 10, 10);
NSBezierPath* circlePath = [NSBezierPath bezierPath];
[circlePath appendBezierPathWithOvalInRect: rect];
Once you have your path you want to set the color for the current drawing context. There are two colors, stroke and fill; stroke is the outline of the path and the fill is the interior color. To set a color you send "set" to an "NSColor" object.
This sets the stroke to black and the fill to red:
[[NSColor blackColor] setStroke];
[[NSColor redColor] setFill];
Now that you have your path and you have your colors set just fill the path and then draw it:
[path stroke];
[path fill];
All of this will need to be done in a graphics context like in drawRect of a view perhaps. All of this together with a graphics context would look like this:
- (void)drawRect:(NSRect)rect
{
// Get the graphics context that we are currently executing under
NSGraphicsContext* gc = [NSGraphicsContext currentContext];
// Save the current graphics context settings
[gc saveGraphicsState];
// Set the color in the current graphics context for future draw operations
[[NSColor blackColor] setStroke];
[[NSColor redColor] setFill];
// Create our circle path
NSRect rect = NSMakeRect(10, 10, 10, 10);
NSBezierPath* circlePath = [NSBezierPath bezierPath];
[circlePath appendBezierPathWithOvalInRect: rect];
// Outline and fill the path
[circlePath stroke];
[circlePath fill];
// Restore the context to what it was before we messed with it
[gc restoreGraphicsState];
}
You may use simple UIView to create perfect circle with only parameter radius:
// Add framework CoreGraphics.framework
#import <QuartzCore/QuartzCore.h>
-(UIView *)circleWithColor:(UIColor *)color radius:(int)radius {
UIView *circle = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 2 * radius, 2 * radius)];
circle.backgroundColor = color;
circle.layer.cornerRadius = radius;
circle.layer.masksToBounds = YES;
return circle;
}
Create an NSView subclass that holds an NSColor as an ivar. In the drawRect method, create an NSBezierPath of the appropriate size, using the view's bounds. Then set the color [myColor set] and fill the path [myPath fill]. There's a lot more you can do, such as set transparency, a border, and so on and so on, but I'll leave that to the docs unless you have a specific question.
To use the NSView subclass, just drag a view object onto your nib, and choose the name of your subclass in custom class in IB's inspector. You'll need to also set an outlet to it in your controller, so you can change the color as needed.
CGContextRef c = UIGraphicsGetCurrentContext();
CGContextSetRGBFillColor(c, 40, 0, 255, 0.1);
CGContextSetRGBStrokeColor(c, 0, 40, 255, 0.5);
// Draw a green solid circle
CGContextSetRGBFillColor(c, 0, 255, 0, 1);
CGContextFillEllipseInRect(c, CGRectMake(100, 100, 25, 25));
Download sketch from apple. http://developer.apple.com/library/mac/#samplecode/Sketch
It can do a lot more, but one of the things is draw circles.