iOS: Text get upside down in drawRect function - drawrect

I am using PDFKit and create a subclass of PDFAnnotation, I want to set my custom padding when I create a free text annotation, so I try to override drawRect function as below
- (void)drawWithBox:(PDFDisplayBox)box inContext:(CGContextRef)context {
UIGraphicsPushContext(context);
CGContextSaveGState(context);
NSAttributedString * mystring = [[NSAttributedString alloc] initWithString:self.contents attributes:#{
NSFontAttributeName: self.font,
NSForegroundColorAttributeName: UIColor.blackColor }];
[mystring drawInRect:CGRectMake(self.bounds.origin.x + 10, self.bounds.origin.y + 10, self.bounds.size.width-20, self.bounds.size.height-20)];
CGContextRestoreGState(context);
UIGraphicsPopContext();
}
The padding works well but the text is upside down.
I try to add the context flipping code based on this link https://forums.developer.apple.com/thread/103683
CGContextTranslateCTM(context, 0.0, self.bounds.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
after the line CGContextSaveGState(context);
But it will make the text disappear! Anyone know the reason or meet the similar issue? Thanks!

I have found the answer, in case someone meets the same problem. Actually the CGContextTransform are based on the page coordinate system, so when you use transform CGContextScaleCTM(context, 1.0, -1.0); the context is outside the page range. What we need to get is X1 = X2; Y1 + Y2 = 2 * (self.bounds.origin.y + self.bounds.size.height / 2), so apply transform CGContextConcatCTM(context, CGAffineTransformMake(1, 0, 0, -1, 0.0, 2 * self.bounds.origin.y + self.bounds.size.height)); works for me.

Related

How to override draw method on PDFAnnotation IOS-PDFKIT

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

UIView drawRect() : fill everything except polygon

I need to fill my UIView in drawRect() method with 'reverted polygon' - everything in view is filled with some color except polygon itself.
I've got this code to draw a simple polygon:
CGContextBeginPath(context);
for(int i = 0; i < corners.count; ++i)
{
CGPoint cur = [self cornerAt:i], next = [self cornerAt:(i + 1) % corners.count];
if(i == 0)
CGContextMoveToPoint(context, cur.x, cur.y);
CGContextAddLineToPoint(context, next.x, next.y);
}
CGContextClosePath(context);
CGContextFillPath(context);
I found a similar question, but in C#, not Obj-C : c# fill everything but GraphicsPath
Probably the fastest way is to set a clip:
// create your path as posted
// but don't fill it (remove the last line)
CGContextAddRect(context, self.bounds);
CGContextEOClip(context);
CGContextSetRGBFillColor(context, 1, 1, 0, 1);
CGContextFillRect(context, self.bounds);
Both the other answers suggest to first fill a rect, then draw the shape with clear color on top. Both omit the necessary blend mode. Here's a working version:
CGContextSetRGBFillColor(context, 1, 1, 0, 1);
CGContextFillRect(context, self.bounds);
CGContextSetBlendMode(context, kCGBlendModeClear);
// create and fill your path as posted
Edit: Both approaches require the backgroundColor to be the clearColor and opaque to be set to NO.
Second Edit: The original question was about Core Graphics. Of course there are other ways of masking part of a view. Most notably CALayer's mask property.
You can set this property to an instance of a CAPathLayer that contains your clip path to create a stencil effect.
in drawRect u can set the background color of ur view with the color u want
self.backgroundColor = [UIcolor redColor]; //set ur color
and then draw a polygon the way u r doing.
CGContextBeginPath(context);
for(int i = 0; i < corners.count; ++i)
{
CGPoint cur = [self cornerAt:i], next = [self cornerAt:(i + 1) % corners.count];
if(i == 0)
CGContextMoveToPoint(context, cur.x, cur.y);
CGContextAddLineToPoint(context, next.x, next.y);
}
CGContextClosePath(context);
CGContextFillPath(context);
hope it helps.. happy coding :)
Create a new CGLayer, fill it with your outside color, then draw your polygon using a clear color.
layer1 = CGLayerCreateWithContext(context, self.bounds.size, NULL);
context1 = CGLayerGetContext(layer1);
[... fill entire layer ...]
CGContextSetFillColorWithColor(self.context1, [[UIColor clearColor] CGColor]);
[... draw your polygon ...]
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextDrawLayerAtPoint(context, CGPointZero, layer1);

drawing dotted lines from UIViewController

I have a UIViewController which has a UIView in it and I want to draw a straight dotted line on it. What is the easiest and performant way to do this? How do I do this? It will just be a straight horizontal dotted line of width 150 with gray color. I've looked around and it seems that using
CGContextSetLineDash is the solution and here's what I have so far:
-(void) drawRect:(CGRect) rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 100);
CGFloat dashes[] = {1,1};
CGContextSetLineDash(context, 2.0, dashes, 2);
CGContextStrokePath(context);
}
CGContextStrokePath draws the current path, however, you never add any path to your context, so nothing is drawn.
Add the following two lines before your CGContextStrokePath call and you should see something:
CGContextMoveToPoint(context, 0, self.bounds.size.height * 0.5);
CGContextAddLineToPoint(context, self.bounds.size.width, self.bounds.size.height * 0.5);
If you haven't already set your stroke color, you should also add something like this (50% gray):
CGContextSetRGBStrokeColor(context, 0.5, 0.5, 0.5, 1.0);
I'm not sure if the result is really what you want, a 100 point dotted line with a pattern of {1, 1} will look more like an rectangle with a pinstripe pattern than like a dotted line. If you want your dots to be circles, you will have to draw those manually in some sort of loop.
Try this:
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [UIColor redColor].CGColor);
CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
CGContextSetStrokeColorWithColor(context, color.CGColor);
CGContextSetLineWidth(context, 2.0);
CGFloat lengths[] = {0, 8};
CGContextSetLineCap(context, kCGLineCapRound);
CGContextSetLineDash(context, 0.0f, lengths, 2);
CGContextMoveToPoint(context, 10, 10);
CGContextAddLineToPoint(context, 200, 10);
CGContextStrokePath(context);
CGColorSpaceRelease(colorspace);

How do I fill color space in a CGrect

I am using the code below and am trying everything to color the resulting circle that it draws, but cannot for the life of me figure it out! Can anyone help? How do I color the circle in that my method draws?
What I have so far is this:
-(void)drawCircleAtPoint:(CGPoint)p withRadius:(CGFloat)radius inContext:(CGContextRef)context
{
UIGraphicsPushContext(context);
CGContextBeginPath(context);
CGContextAddArc(context, p.x, p.y, radius, 0, 2*M_PI, YES);
CGContextStrokePath(context);
UIGraphicsPopContext();
}
- (void)drawRect:(CGRect)rect
{
CGPoint midPoint;
midPoint.x = self.bounds.origin.x + self.bounds.size.width/2;
midPoint.y = self.bounds.origin.y + self.bounds.size.width/2;
CGFloat size = self.bounds.size.width/2;
if (self.bounds.size.height < self.bounds.size.width) size = self.bounds.size.height/2;
size *= 0.90;
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 10.0);
[[UIColor blackColor] setStroke];
CGColorRef color = [[UIColor redColor] CGColor];
CGContextSetFillColorWithColor(context, color);
CGContextFillPath(context);
[self drawCircleAtPoint:midPoint withRadius:size inContext:context];
}
You're trying to fill the current path, but you haven't added anything to it yet. The only point at which you add anything to the current path is in drawCircleAtPoint:withRadius:inContext:, after which you only stroke it.
Move your CGContextFillPath call into that method. You'll want to fill before you stroke, since the stroke will reset the current path. (If you want to fill on top of the inside of the stroke, you'll need to save the gstate before stroking and restore it after, so that the current path is still set when you fill.)

images not showing in pdf preview?

Strange feeling that if i want to preview a pdf on a controller alongside an image the only way i can get it to work is to use a defined segue from the previous view controller, maybe i'm not clear. How to i get an image to show up on a preview of a QuickLook action without having to segue in and not just modal into the view ? Is this logical ?
I dev an app which generates PDF's, and i'm giving the user the possibility to preview them....but when these have an image....boom the image disappears from the preview...unless i use a segue then no problem, seems weird though. Any suggestions ?....thanks in advance...
- (CFRange)renderPage:(NSInteger)pageNum withTextRange:(CFRange)currentRange
andFramesetter:(CTFramesetterRef)framesetter
{
CGContextRef currentContext = UIGraphicsGetCurrentContext();
// Put the text matrix into a known state. This ensures
// that no old scaling factors are left in place.
CGContextSetTextMatrix(currentContext, CGAffineTransformIdentity);
// Create a path object to enclose the text. Use 72 point
// margins all around the text.
#pragma-----------here to disclose the height of text on pdf preview
CGRect frameRect = CGRectMake(40, 72, 468, 350);
CGMutablePathRef framePath = CGPathCreateMutable();
CGPathAddRect(framePath, NULL, frameRect);
[_imageView1 setImage:theImage1];
CGRect frame = CGRectMake(40, 50, 130, 130);
[ProgramPDFViewController drawImage:theImage1 inRect:frame];
CGSize size = theImage1.size;
CGFloat ratio = 0;
if (size.width > size.height) {
ratio = 44.0 / size.width;
} else {
ratio = 44.0 / size.height;
}
CGRect rect = CGRectMake(0.0, 0.0, ratio * size.width, ratio * size.height);
UIGraphicsBeginImageContext(rect.size);
[theImage1 drawInRect:rect];
UIGraphicsEndImageContext();
CTFrameRef frameRef = CTFramesetterCreateFrame(framesetter, currentRange, framePath, NULL);
CGPathRelease(framePath);
CGContextTranslateCTM(currentContext, 0, kDefaultPageHeight);
CGContextScaleCTM(currentContext, 1.0, -1.0);
CTFrameDraw(frameRef, currentContext);
currentRange = CTFrameGetVisibleStringRange(frameRef);
currentRange.location += currentRange.length;
currentRange.length = 0;
CFRelease(frameRef);
return currentRange;
}