Rendering UIView to image when view draws outside its bounds - ios

I want to render a UIView to an image. My go-to UIView category for this is
- (UIImage *)num_renderToImage
{
UIGraphicsBeginImageContext(self.bounds.size);
[self.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
However in this case, the UIView has elements that draw outside its bounds and the above clips them. Altering the size passed to UIGraphicsBeginImageContext doesn't help since the size grows down and to the right, but these elements are above and to the left. What's the right way to do this?

In the scenario above, with a UIView clipping a UIButton that draws outside its bounds, you might try:
- (IBAction)snapshot:(id)sender {
UIButton *button = sender;
UIView *v = button.superview;
// Prepare the rectangle to be drawn
CGRect allTheViews = CGRectUnion(v.bounds, button.frame);
UIGraphicsBeginImageContext(allTheViews.size);
CGContextRef context = UIGraphicsGetCurrentContext();
// This is what you differently
CGContextTranslateCTM(context, -allTheViews.origin.x, -allTheViews.origin.y);
// This part is the same as before
[v.layer renderInContext:context];
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[UIImagePNGRepresentation(img) writeToFile:#"/tmp/foo.png" atomically:NO];
}
Here we're taking the union of what we want to draw, then translating the CTM so it's in view in the graphics context we're drawing into.
(This example, in particular, is hooked up to the action of the button and writes the UIImage of the button and containing view out to a file. You can adjust as your needs require.)

I made this more general version in Swift 5.0 if anyone has the problem. Just set the offset to the value you want :
private func snapshotView(cell:UIView) -> UIImageView
{
// by how much extra point out of the view bounds you want to draw your snapshot
let offset:CGFloat = 10
let frame = cell.bounds.union(CGRect(x:-offset * 2.0,y:-offset * 2.0,
width:cell.bounds.width,
height: cell.bounds.height)).size
UIGraphicsBeginImageContextWithOptions(frame, false ,0)
let context = UIGraphicsGetCurrentContext()
context!.translateBy(x: offset, y: offset);
cell.layer.render(in: context!)
let snapshotImage:UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return UIImageView(image: snapshotImage)
}

Related

How to draw circle on UIImageView without adding layer in objective-c?

I want to draw a circle on UIImageView. I have tried it but it didn't work.
This is a example image of what i want to achieve:
The circle should be drawn on where user taps on UIImageView and I want to do it without adding any sublayer.
Is it some way to do this?
so far i have used this code from the internet but it didn't worked.
- (UIImage *)imageByDrawingCircleOnImage:(UIImage *)image
pointX:(float) x
PointY:(float) y
{
// begin a graphics context of sufficient size
UIGraphicsBeginImageContext(image.size);
// draw original image into the context
[image drawAtPoint:CGPointZero];
// get the context for CoreGraphics
CGContextRef ctx = UIGraphicsGetCurrentContext();
// set stroking color and draw circle
[[UIColor redColor] setStroke];
// make circle rect 5 px from border
CGRect circleRect = CGRectMake(0, 0,
image.size.width,
image.size.height);
circleRect = CGRectInset(circleRect, x, y);
// draw circle
CGContextStrokeEllipseInRect(ctx, circleRect);
// make image out of bitmap context
UIImage *retImage = UIGraphicsGetImageFromCurrentImageContext();
// free the context
UIGraphicsEndImageContext();
return retImage;
}
Suppose your object's view is square, Set the cornerRadius to half of the width or height.
maskToBounds set your image as per shape of rounded imageView
For example,add this code for your requirement,
yourImageView.layer.cornerRadius = yourImageView.imageView.frame.size.height /2;
yourImageView.layer.masksToBounds = YES;
Hope this will help you :)

Crop image enclosed in a 4 sided (not rectangle) polygon

How to crop image portion that is enclosed inside a random polygon (4 sided but not rectangle). Just wanted to know which method to follow not the code.
You can do this easily in Core Graphics.
You just need to create a new image context, add the path to the context, then crop the context to the path. You can then draw your image in this and get out a cropped image.
-(UIImage*) cropImage:(UIImage*)image withPath:(UIBezierPath*)path { // where the UIBezierPath is defined in the UIKit coordinate system (0,0) is top left
CGRect r = CGPathGetBoundingBox(path.CGPath); // the rect to draw our image in (minimum rect that the path occupies).
UIGraphicsBeginImageContextWithOptions(r.size, NO, image.scale); // begin image context, with transparency & the scale of the image.
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(ctx, -r.origin.x, -r.origin.y); // translate context so that when we add the path, it starts at (0,0).
CGContextAddPath(ctx, path.CGPath); // add path.
CGContextClip(ctx); // clip any future drawing to the path region.
[image drawInRect:(CGRect){CGPointZero, image.size}]; // draw image
UIImage* i = UIGraphicsGetImageFromCurrentImageContext(); // get image from context
UIGraphicsEndImageContext(); // clean up and finish context
return i; // return image
}
For example, if we take a screenshot of your question (I couldn't find any other images lying about!)
and use the following code....
UIImage* i = [UIImage imageNamed:#"3.png"];
UIBezierPath* p = [UIBezierPath bezierPath];
[p moveToPoint:CGPointMake(0, 0)];
[p addLineToPoint:CGPointMake(1500, 500)];
[p addLineToPoint:CGPointMake(500, 1200)];
UIImage* i1 = [self cropImage:i withPath:p];
This would be the output...
You could even add this to a UIImage category if you're going to be cropping images regularly.
Updated for Swift 3.
I noticed that a lot of the implementations out there seem to want the background to be white or transparent, and I really needed just wanted the background color to be black.
extension UIImage {
func crop(withPath: UIBezierPath, andColor: UIColor) -> UIImage {
let r: CGRect = withPath.cgPath.boundingBox
UIGraphicsBeginImageContextWithOptions(r.size, false, self.scale)
if let context = UIGraphicsGetCurrentContext() {
let rect = CGRect(origin: .zero, size: size)
context.setFillColor(andColor.cgColor)
context.fill(rect)
context.translateBy(x: -r.origin.x, y: -r.origin.y)
context.addPath(withPath.cgPath)
context.clip()
}
draw(in: CGRect(origin: .zero, size: size))
guard let image = UIGraphicsGetImageFromCurrentImageContext() else {
return UIImage()
}
UIGraphicsEndImageContext()
return image
}
}

How to draw image at angle in IOS

I want to draw a image at angle. I have one ImageView with contect mode Aspect to fit. Also I have one dragView which user can move and rotate on the View. I have moved the dragView with CGAffineTransformMakeRotation
Both the ImageView and dragView is added on the main (self) view.
I have done the following code.
It has two problem
I dont know how to save image at angle that i can get from the CGAffineTransformMakeRotation
dragView is not placed currently on the image. it has some variation in the x and y position.
-(void)btnSaveClicked{
UIGraphicsBeginImageContextWithOptions(imgView.image.size, NO, 0.0); // create context as the image size
UIImage *dragImage = [self imageWithView:dragView]; // convert image from the dragView
[imgView.image drawInRect:CGRectMake(0, 0, imgView.image.size.width, imgView.image.size.height)]; // draw main image
CGSize size = imageDraw.size;
float factor = imgView.image.size.width / imgView.frame.size.width;
if (factor < imgView.image.size.height / imgView.frame.size.height) {
factor = imgView.image.size.height / imgView.frame.size.height;
}
CGRect rect = CGRectMake(self.view.frame.size.width/2-size.width/factor/2, self.view.frame.size.height/2-size.height/factor/2, size.width/factor, size.height/factor);
CGPoint point = CGPointMake((dragView.frame.origin.x+10-rect.origin.x)*factor,(dragView.frame.origin.y+10 - rect.origin.y)*factor);
[dragImage drawAtPoint:point blendMode:kCGBlendModeNormal alpha:1.0]; // draw the dragImnage but the point is wrong
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
imgView.image = newImage;
}
Sample Project : https://github.com/SunnyShah407/WaterMark/tree/master

Convert CGPoint from CGRect to other CGRect

I have placed one label on view and view's frame is CGRectMake(0,0,270,203). Now I have to take screen shot of view with CGRectMake(0,0,800,600). So I have to convert label from old rect to new rect.
here is code which I used to take screen shot:
CGRect rect = [view bounds];
UIGraphicsBeginImageContextWithOptions(rect.size,YES,0.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
[view.layer renderInContext:context];
UIImage *capturedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
Here is the code which I used to convert point:
CGRect f = [view convertRect:lblMessage.frame fromView:viewMessage];
But I am not able to get actual position from label in new image. Can anyone help where I am wrong.
Here I have attached image for more clarification.
In small view, I have add one label and I have to convert label's frame as per big image view.
Thanks,
+(UIImage*)screenShotOf:(UIView*)view atScale:(CGFloat)scale
{
UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.opaque, scale);
[view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage * img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return img;
}
Here You need to adjust the scale property as you needed.
You could do something like this:
-(CGPoint) convertPoint: (CGPoint) point fromRect: (CGRect) fromRect toRect: (CGRect) toRect {
return (CGPoint){
(toRect.size.width/fromRect.size.width) * point.x,
(toRect.size.height/fromRect.size.height) * point.y
};
}

How to apply UIScrollView's transform to UIView?

In my app I have a scrollview with an added subview called allView.
In the scrollview delegate methods I am applying value of the current transformation of the scrollview's subview to another view called paint view
paintView.transform = allView.transform
and save it to the disk.
The image that is created in result of that process looks different than the one on the screen. Why? How can I fix it?
View Controller
- (void)scrollViewDidScroll:(UIScrollView *)scrollView; {
self.paintView.transform =self.allView.transform;
[self.paintView setNeedsDisplay];
}
- (void)scrollViewDidZoom:(UIScrollView *)scrollView{
self.backgroundView.transform = self.allView.transform;
[self.paintView setNeedsDisplay];
}
Paint View
Inside the PaintView's draw rect I am trying to apply transformation from the scroll view and
- (void)drawRect:(CGRect)rect
{
// Drawing code
// Draw on the screen
CGContextRef ctx1 =UIGraphicsGetCurrentContext();
CGContextConcatCTM(ctx1, self.transform);
CGColorRef wh = [[UIColor redColor]CGColor];
CGContextSetStrokeColorWithColor(ctx1, wh);
CGContextMoveToPoint(ctx1, 0, 0);
CGContextAddLineToPoint(ctx1, 200, 200);
CGContextStrokePath(ctx1);
// Apply scroll view's transformation
CGRect r = CGRectApplyAffineTransform(rect,self.transform );
//that gives a resized image
UIGraphicsBeginImageContextWithOptions(r.size, NO, 0.0);
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextConcatCTM(ctx, self.transform);
// stroke and so on
CGContextSetStrokeColorWithColor(ctx, wh);
CGContextMoveToPoint(ctx, 0, 0);
CGContextAddLineToPoint(ctx, 200, 200);
CGContextStrokePath(ctx);
Getting image with entire content.
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
//Clipping the image
CGImageRef cgImg = CGImageCreateWithImageInRect(image.CGImage, rect);
UIImage *img = [UIImage imageWithCGImage:cgImg];
NSData * d = UIImageJPEGRepresentation(img, 0.8);
//saving the image (for debugging)
[self save:d];
UIGraphicsEndImageContext();
}
iOS Simulator
Image saved to the disk
It may be a little bit hard to diagnose the problem here without being able to have running code. However, I think the problem may be on these lines where you are clipping the image:
// Clipping the image
CGImageRef cgImg = CGImageCreateWithImageInRect(image.CGImage, rect);
UIImage *img = [UIImage imageWithCGImage:cgImg];
I think you actually just want to get the scroll view's visible rect, which would be scrollView.frame rather than the subview that fills the scroll view's entire contentSize. So, using some way (such as a property on paintView such as a visibleFrame), I would revise the lines to be something like this:
// Clipping the image
CGImageRef cgImg = CGImageCreateWithImageInRect(image.CGImage, self.visibleFrame);
UIImage *img = [UIImage imageWithCGImage:cgImg];
Hope this helps you get passed this issue!

Resources