I want to draw on a UIView that has a background image that I set using the code below:
- (void)setBackgroundImageFromData:(NSData *)imageData {
UIImage *image = [UIImage imageWithData:imageData];
int width = image.size.width;
int height = image.size.height;
CGSize size = CGSizeMake(width, height);
CGRect imageRect = CGRectMake(0, 0, width, height);
UIGraphicsBeginImageContext(size);
CGContextRef currentContext = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(currentContext, 0, height);
CGContextScaleCTM(currentContext, 1.0, -1.0);
CGContextDrawImage(currentContext, imageRect, image.CGImage);
UIGraphicsEndImageContext();
}
The initial view is created using the code from Apple's GLPaint example. For the life of me, that background image is not shown. What am I missing?
Thanks!
You create a UIImage, and an imageRect successfully. You then begin a image context to draw the image to, draw the image to the context, and end the context. The problem is that you just allow the context to expire without doing anything to it.
In UIKit you don't push new visuals upwards, you wait until requested to draw. Internal mechanisms cache your images and use them to move things about and otherwise redraw the screen at the usual 60fps.
If this is a custom UIView subclass, then you probably want to keep hold of the UIImage and composite it as part of your drawRect:. You can set the contents of your UIView as having changed by calling setNeedsRedraw — you'll then be asked to redraw your contents at some point in the future.
If this isn't a custom subclass, then the easiest thing to do is to wrap this view in an outer view and add a UIImageView behind it, to which you can set the UIImage.
Related
I've been struggling with this method for a while. I am drawing an avatar on top of another image. The user picture I want to be a circle, however I can't seem to figure out how. The user picture is a UIImage and not a UIImageView. I am aware of how to make a circle if it is an imageview. Below is the code. There might be a better approach.
-(UIImage *)drawImage:(UIImage*)pinImage withBadge:(UIImage *)user{
UIGraphicsBeginImageContextWithOptions(pinImage.size, NO, 0.0f);
[pinImage drawInRect:CGRectMake(0, 0, pinImage.size.width, pinImage.size.height)];
[user drawInRect:CGRectMake(20.0, 10.0, user.size.width/2, user.size.height/2)];
UIImage *resultImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return resultImage;
}
The result is good, but the user image is still square, it is not circle. I have tried making the add the User image to a UIImageView, transform it to a circle, and then use it in the method by calling yourImageView.image, but no luck. I also tried numerous other ways. My logic is more than likely incorrect.
The desired outcome is a rounded image place on top of a pin/annotation. Where the black dot would be an image (a bigger circle than this).
You can clip the image context to the path of an image
// Start the image context
UIGraphicsBeginImageContextWithOptions(pinImage.size, NO, 0.0);
UIImage *resultImage = nil;
// Get the graphics context
CGContextRef context = UIGraphicsGetCurrentContext();
// Draw the first image
[pinImage drawInRect:CGRectMake(0, 0, pinImage.size.width, pinImage.size.height)];
// Get the frame of the second image
CGRect rect = CGRectMake(20.0, 10.0, user.size.width/2, user.size.height/2)
// Add the path of an ellipse to the context
// If the rect is a square the shape will be a circle
CGContextAddEllipseInRect(context, rect);
// Clip the context to that path
CGContextClip(context);
// Do the second image which will be clipped to that circle
[user drawInRect:rect];
// Get the result
UIImage *resultImage = UIGraphicsGetImageFromCurrentImageContext();
// End the image context
UIGraphicsEndImageContext();
Create a circular path and then clip to that?
CGContextAddArc(ctx, ....);
CGContextClip(ctx);
After successfully using UIView’s new drawViewHierarchyInRect:afterScreenUpdates: method introduced in iOS 7 to obtain an image representation (via UIGraphicsGetImageFromCurrentImageContext()) for blurring my app also needed to obtain just a portion of a view. I managed to get it in the following manner:
UIImage *image;
CGSize blurredImageSize = [_blurImageView frame].size;
UIGraphicsBeginImageContextWithOptions(blurredImageSize, YES, .0f);
[aView drawViewHierarchyInRect: [aView bounds] afterScreenUpdates: YES];
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
This lets me retrieve aView’s content following _blurImageView’s frame.
Now, however, I would need to obtain a portion of aView, but this time this portion would be “inside”. Below is an image representing what I would like to achieve.
I have already tried creating a new graphics context and setting its size to the portion’s size (red box) and calling aView to draw in the rect that represents the red box’s frame (of course its superview’s frame being equal to aView’s) but the image obtained is all black (empty).
After a lot of tweaking I managed to find something that did the job, however I heavily doubt this is the way to go.
Here’s my [edited-for-Stack Overflow] code that works:
- (UIImage *) imageOfPortionOfABiggerView
{
UIView *bigViewToExtractFrom;
UIImage *image;
UIImage *wholeImage;
CGImageRef _image;
CGRect imageToExtractFrame;
CGFloat screenScale = [[UIScreen mainScreen] scale];
// have to scale the rect due to (I suppose) the screen's scale for Core Graphics.
imageToExtractFrame = CGRectApplyAffineTransform(imageToExtractFrame, CGAffineTransformMakeScale(screenScale, screenScale));
UIGraphicsBeginImageContextWithOptions([bigViewToExtractFrom bounds].size, YES, screenScale);
[bigViewToExtractFrom drawViewHierarchyInRect: [bigViewToExtractFrom bounds] afterScreenUpdates: NO];
wholeImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
// obtain a CGImage[Ref] from another CGImage, this lets me specify the rect to extract.
// However since the image is from a UIView which are all at 2x scale (retina) if you specify a rect in points CGImage will not take the screen's scale into consideration and will process the rect in pixels. You'll end up with an image from the wrong rect and half the size.
_image = CGImageCreateWithImageInRect([wholeImage CGImage], imageToExtractFrame);
wholeImage = nil;
// have to specify the image's scale due to CGImage not taking the screen's scale into consideration.
image = [UIImage imageWithCGImage: _image scale: screenScale orientation: UIImageOrientationUp];
CGImageRelease(_image);
return image;
}
I hope this will help anyone that stumped upon my issue. Feel free to improve my snippet.
Thanks
In my project I have to make a screenshot of the screen and apply blur to create the effect of frosted glass. Content can be moved under the glass and blured picture changed.
I'v used Accelerate.framework to speedup blurring, also i,v used OpenGL to draw CIImage directly to GLView.
Now I'm looking for a way to optimize getting screenshot of the screen.
I use this method to get screenshot of some area at the bottom of the screen:
CGSize size = CGSizeMake(rect.size.width, rect.size.height);
// get screenshot of self.view
CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
CGContextRef ctx = CGBitmapContextCreate(nil, size.width, size.height, 8, 0, colorSpaceRef, kCGImageAlphaPremultipliedFirst);
CGContextClearRect(ctx, rect);
CGColorSpaceRelease(colorSpaceRef);
CGContextSetInterpolationQuality(ctx, kCGInterpolationNone);
CGContextSetShouldAntialias(ctx, NO);
CGContextSetAllowsAntialiasing(ctx, NO);
CGContextTranslateCTM(ctx, 0.0, someView.frame.size.height);
CGContextScaleCTM(ctx, 1, -1);
//add mask
CGImageRef maskImage = [UIImage imageNamed:#"mask.png"].CGImage;
CGContextClipToMask(ctx, rect, maskImage);
[someView.layer renderInContext:ctx];
//get screenshot image
CGImageRef imageRef = CGBitmapContextCreateImage(ctx);
It works fine and fast if self.view has 1-2 subviews, but if there are several subviews (or it is tableview), then everything starts to slow down.
So i try to find a fast way to get pixels from some rect on screen. Maybe using a low-level API.
if just perform some animations , try this way , called -snapshotViewAfterScreenUpdates: or -resizableSnapshotViewFromRect:afterScreenUpdates:withCapInsets: method which are UIView provided , these method return UIView object without rendering into a bitmap image , so it is a more efficient .
I've found some code which gives me a UIImage out of a PDF-File. It works, but I have two questions:
Is there a possibility to achieve a better quality of the UIImage? (See Screenshot)
I only see the first page in my UIImageView. Do I have to embed the file in a UIScrollView to be complete?
Or is it better to render just one page and use buttons to navigate through the pages?
P.S. I know that UIWebView can display PDF-Pages with some functionalities but I need it as a UIImage or at least in a UIView.
Bad quality Image:
Code:
-(UIImage *)image {
UIGraphicsBeginImageContext(CGSizeMake(280, 320));
CGContextRef context = UIGraphicsGetCurrentContext();
CFURLRef pdfURL = CFBundleCopyResourceURL(CFBundleGetMainBundle(), CFSTR("ls.pdf"), NULL, NULL);
CGPDFDocumentRef pdf = CGPDFDocumentCreateWithURL((CFURLRef)pdfURL);
CGContextTranslateCTM(context, 0.0, 320);
CGContextScaleCTM(context, 1.0, -1.0);
CGPDFPageRef page = CGPDFDocumentGetPage(pdf, 4);
CGContextSaveGState(context);
CGAffineTransform pdfTransform = CGPDFPageGetDrawingTransform(page, kCGPDFCropBox, CGRectMake(0, 0, 280, 320), 0, true);
CGContextConcatCTM(context, pdfTransform);
CGContextDrawPDFPage(context, page);
CGContextRestoreGState(context);
UIImage *resultingImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return resultingImage;
}
I know i'm a little late here, but i hope i can help someone else looking for an answer.
As to the questions asked:
I'm afraid the only way to achieve a better image quality is to render a bigger image, and letting the UIImageView resize it for you. I don't think you can set the resolution, but using a bigger image may be a good choice. It won't take too long for the page to render, and the image will have a better quality. PDF files are rendered on demand depending on the zoom level, that's why they seem to have "better quality".
As to rendering all the pages, you can get the number of pages in the document calling CGPDFDocumentGetNumberOfPages( pdf ) and using a simple for loop you can concat all the images generated in one single image. For displaying it, use the UIScrollVIew.
In my opinion, this approach is better than the above, but you should try to optimize it, for example rendering always the current, the previous and the next page. For nice scrolling transition effects, why not use a horizontal UIScrollView.
For more generic rendering code, i always do the rotation like this:
int rotation = CGPDFPageGetRotationAngle(page);
CGContextTranslateCTM(context, 0, imageSize.height);//moves up Height
CGContextScaleCTM(context, 1.0, -1.0);//flips horizontally down
CGContextRotateCTM(context, -rotation*M_PI/180);//rotates the pdf
CGRect placement = CGContextGetClipBoundingBox(context);//get the flip's placement
CGContextTranslateCTM(context, placement.origin.x, placement.origin.y);//moves the the correct place
//do all your drawings
CGContextDrawPDFPage(context, page);
//undo the rotations/scaling/translations
CGContextTranslateCTM(context, -placement.origin.x, -placement.origin.y);
CGContextRotateCTM(context, rotation*M_PI/180);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextTranslateCTM(context, 0, -imageSize.height);
Steipete already mentioned setting the white background:
CGContextSetRGBFillColor(context, 1, 1, 1, 1);
CGContextFillRect(context, CGRectMake(0, 0, imageSize.width, imageSize.height));
So the last thing to keep in mind is when exporting an image, set the quality to the maximum. For example:
UIImageJPEGRepresentation(image, 1);
What are you doing with the CGContextTranslateCTM(context, 0.0, 320); call?
You should extract the proper metrics form the pdf, with code like this:
cropBox = CGPDFPageGetBoxRect(page, kCGPDFCropBox);
rotate = CGPDFPageGetRotationAngle(page);
Also, as you see, the pdf might has rotation info, so you need to use the CGContextTranslateCTM/CGContextRotateCTM/CGContextScaleCTM depending on the angle.
You also might wanna clip any content that is outside of the CropBox area, as pdf has various viewPorts that you usually don't wanna display (e.g. for printers so that seamless printing is possible) -> use CGContextClip.
Next, you're forgetting that the pdf reference defines a white background color. There are a lot of documents out there that don't define any background color at all - you'll get weird results if you don't draw a white background on your own --> CGContextSetRGBFillColor & CGContextFillRect.
I have a UITableView with a set of UITableViewCells in it - each of the cells contain a UIView which shows some graphics in a context.
What would be the best way to export these cells into one UIImage?
Thanks
edit 1: I know how to create an image from the viewable area of the table, but this table scrolls out of the screen and I would like to create a UIImage of all of the cells, not only those you see.
You should be able to do this by telling the view's layer to draw into a custom graphics context, then creating a bitmapped CGImage from that context. Once you have a CGImage, you can create a UIImage from it. It would look roughly like this:
// Create a bitmap context.
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef bitmapContextForCell = CGBitmapContextCreate(nil, cell.bounds.size.width, cell.bounds.size.height, 8, 0, colorSpace, kCGImageAlphaNone);
CGColorSpaceRelease(colorSpace);
// Draw the cell's layer into the context.
[cell.layer renderInContext:bitmapContextForCell];
// Create a CGImage from the context.
CGImageRef cgImageForCell = CGBitmapContextCreateImage(bitmapContextForCell);
// Create a UIImage from the CGImage.
UIImage * cellImage = [UIImage imageWithCGImage:cgImageForCell];
// Clean up.
CGImageRelease(cgImageForCell);
CGContextRelease(bitmapContextForCell);
That's how to create a image for each cell. If you want to create one image for all your cells, use your table view instead of the cell.