I'd like to render a basic UIListView into an image I can save.
I found lots of code here for rendering a UIView to an image (for iphone/iPod Touch). In fact, the code below is what I pulled from other posts and am using now. Problem is it doesn't draw anything from the UIListView which isn't visible when the method is invoked.
- (void)snapShotAction:(id)sender
{
CGRect screenRect = [[UIScreen mainScreen] bounds];
UIGraphicsBeginImageContext(screenRect.size);
CGContextRef ctx = UIGraphicsGetCurrentContext();
[[UIColor blackColor] set];
CGContextFillRect(ctx, screenRect);
//[self.view.layer renderInContext:ctx];
[self.myTableView.layer renderInContext:ctx];
UIImage *screenImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIImageWriteToSavedPhotosAlbum(screenImage, nil, nil, nil);
}
So I'd like the flexibility of being able to force the entire UIListView to render (including offscreen rows) to an image, but if no one has some insight for making that happen, I guess my alternative is to create an image or graphicscontext and just render a facsimile of the list manually (and any references to sample code for doing that are appreciated).
Here's another idea that occurred to me. Perhaps I can use a separate UITableView with no bottom or top bars (to maximize horizontal space) then calculate the row heights based on the number of items in the table then use the code above to capture that. Of course there will be limits to how many items the list can have before the text size is too small to be usable...
The problem lies in the way UIListView works. In order to save memory, it doesn't render a cell until just before it's going to be displayed, so any UIListViewCell that is off screen may not even be rendered. Short of digging in and injecting code into UIListView to make it render differently (probably not a good idea), you're going to have to stitch it together by scrolling down the list.
Here's an idea you can try that probably won't work: try changing the bounds of the window and all relevant views to be larger than the screen, large enough to hold the entire UIListView at once. Assuming that you're running on the simulator this shouldn't kill the memory. However, I have a feeling that UIListView is smarter than to be fooled by that and bases how much it renders in part on the size of the screen. But it's probably worth a shot.
Related
My app lets users choose or take a picture then add other objects or text on top and rotate/resize them.
For saving i'm just taking a screenshot of the iPhone screen because after trying for hours and hours I just couldn't figure out how to save the original image with the added objects being placed at the right spots, with the right rotation/resized/etc... (If anyone knows a good example/tutorial of how to do this it would be incredibly helpful!)
I have a UIView with a size of 320x366. When user chooses an image I load it inside that UIView and it gets sized to fit properly with it's aspect ratio. When the user is done adding/editing objects on his image he can then save it.
-(UIImage *)createMergedImage
{
UIGraphicsBeginImageContextWithOptions(CGSizeMake(contentView.frame.size.width, contentView.frame.size.height), NO, 0.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextClearRect(context, CGRectMake(0, 0, contentView.frame.size.width,contentView.frame.size.height));
//contentView is the 320x366 view with all the images
[contentView.layer renderInContext:context];
UIImage *screenShot = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return screenShot;
}
That's the code i'm using to save the UIView as a picture. Fitting the images with their correct shrunk aspect ratio, there's either transparent border at the top/bottom or left/right.
Now it's saving and works, when I open the image it's exactly what i'd expect. The problem is when i'm looking at the preview image, it shows other images (that i've previously seen on my iPhone) in the transparent part of the picture. As you can see in this following image.
When I go in the Camera Roll the transparent part looks black (like it should) as seen in this second image.
Also when i'm scrolling through my Camera Roll when I get to the image that my app saved, i'll see those extra random images in the transparent area for 0-1 secs before it disappears and becomes black (leaving the correct image the way it should be).
I'm hoping someone has seen something like this before and knows how to fix it.
Thanks!
I am receiving a series of images over a network connection, and want to display them as fast as possible, up to 30 FPS. I have derived a UIView object (so I can override drawRect) and exposed a UIImage property called cameraImage. The frame data is arriving as fast as needed, but the actual drawing to screen takes far too much time (as a result of the lag from setNeedsDisplay), both creating lag in the video and slowing down the interaction with other controls. What is a better way to do this? I've though of using OpenGL, but the only examples I've seen aren't for drawing static images to the screen, but for adding texture to some rotating polygon.
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGRect screenBounds = [[UIScreen mainScreen] bounds];
[self.cameraImage drawInRect:screenBounds];
// Drawing code
//CGContextDrawImage(context, screenBounds, [self.cameraImage CGImage]); // Doesn't help
}
Try setting a CALayer's contents to the image. Or, if you set it up with relatively sane parameters, setting a UIImageView's image shouldn't be much slower.
OpenGL is definitely an alternative, but it's much more difficult to work with, and it will still be limited by the same thing as the others: the texture upload speed.
(The way you're doing it (drawing in drawRect:), you have to let UIKit allocate a bitmap for you, then draw the image into the bitmap, then upload the bitmap to the video hardware. Best to skip the middleman and upload the image directly, if you can.)
It may also depend on where the CGImage came from. Is it backed by raw bitmap data (and if so, in what format?), or is it a JPEG or PNG? If the image has to be format-converted or decoded, it will take longer.
Also, how large is the CGImage? Make sure it isn't larger than it needs to be!
I've been stuck at this for quite some time. I've looked at many other answers and also at Apple's examples.
What I want to know is how I can go about loading large images in UIScrollView, and be able to page through them and zoom in to them, like the Photos app.
I'm working on an app where I need to scroll through large images (by large, I mean images greater than 1024 x 1024). I've already implemented something similar to what the photos app does. However, now that I'm using large images, I get memory warnings and stuff. I know why I'm getting the warning, and I know that I need to Tile the images.
Apple's examples demonstrate tiling with small images which are already present. My app will be downloading multiple images, saving them on a disk and the user will be able to look at them at a later time. Thus, I cannot have the images cut up using other programs.
I need something wherein I can tile the full size images. Any help will be greatly appreciated. Additionally, as I mentioned earlier, I know that I have to do something with Tiling. However, I'm new to this, and I would greatly appreciate if the answers contain some sample code, which I can use as a start off point.
I've looked at other questions, and none seemed to have been answered to my satisfaction, owing to which, I'm asking this question again.
Thanks again!
To work with tiling you'll need to use a CaTiledLayer in your view. Googling for it will give you good information. Basically you declare that the UIVIew is using it, declare some levels of details for zooming and in the drawRect you'll receive each tile as the rect parameter.
To tile the image, load in a UIImage (it uses memory, but quite less than showing it) and use for each tile you want something like:
UIGraphicsBeginImageContextWithOptions(tileSize, YES, 1);
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(ctx, -tileSize.width * columnNumber, -tileSize.height*rowNumber);
[img drawInRect:CGRectMake(0, 0, tileSize.width, tileSize.height)];
UIImage* imgCut = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
and save imgCut. You probably will want to generate tiling with different zooms if you use different level of detail in your CATiledLayer.
I need to mask a "texture" image with a rotated greyscale image.
I found out, that I have to do it with CGImages or CGlayers (if there is a simplier way using UIImageViews only, please let me know about it).
My problem is simple:
The antialias of any
rotation-transformed CG stuff is quiet
jaggy...
... but the antialias of a rotation-transformed UIImageView is kinda perfect. How can I produce that beautiful antialiased rotations?
I've uploaded a "proof" involving actual iPhone Simulator screenshots, to see what am I talkin' about: http://gotoandplay.freeblog.hu/files/Proof.png
I've tried to use CGImages, CGLayers, UIImageViews "captured" with renderInContext, I've tried to CGContextSetInterpolationQuality to high, and also tried to set CGContextSetAllowsAntialiasing - CGContextSetShouldAntialias, but every case returned the same jaggy result.
I'm planning to learn using OpenGL next year, but this development should released using CoreGraphics only. Please let me know how to get a perfectly rendered rotated image, I just can't accept it's impossible.
To add 1px transparent border to your image use this
CGRect imageRect = CGRectMake(0, 0, image.size.width, image.size.height);
UIGraphicsBeginImageContext( imageRect.size );
[image drawInRect:CGRectMake(1,1,image.size.width-2,image.size.height-2)];
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
Have you tried adding a clear, 1 pixel border around your image? I've heard of that recommended as a trick to avoid aliasing, by giving CoreGraphics some room to work with when blending the edges.
I am having a similar problem, looks like I'm going to move it over to OpenGL ES as well. I can't nail down an effective solution that doesn't hurt performance.
For reference of future CoreGraphics explorers, putting a 1-pixel transparent border did make for a noticeable improvement in my experiments, but it appears that as Eonil mentioned, you end up with multiple stages of antialiasing/smoothing/interpolation working against each other. IE: CGLayer does some interpolation for it's rotation, then context it's being drawn to will do some interpolation/antialiasing, so on so forth until it ends up looking pretty rough.
I actually ended up with better results by disabling interpolation and antialiasing on the destination context, though it was still obviously jaggy (less artifacts overall though). I was able to achieve the best overall appearance by enabling interpolation and antialiasing when constructing the CGLayer, and disabling it for the destination context when re-drawing it. This approach, obviously, is fraught with other problems.
I was looking at some animations in my iPhone app and felt like it was kind of ugly. And then I undertsood it: it just doesn't animate through subpixel states.
So, in case I use usual +beginAnimations/+commitAnimations, moving some stuff just a few pixels look "jumpy". How can I avoid it? Are there any flags to make it animate through float coords or whatever?
Just to give you an idea of what I have and what I'm looking for, please refer to the picture:
alt text http://www.ptoing.net/subpixel_aa.gif
Thanks in advance,
Anton
That's funny, but I found that UIImageView animate its content using aniti-aliasing, while edges of the view are not anti-aliased (hard). It seems to be because UIView itself should maintain the same bounds, while subpixel rendering might add to the bound a bit.
So, I ended up just putting an image with some transparent space around the picture, and it all went smooth.
Just don't let UIView cut its contents for you :)
You can try deriving the item you're animating from a custom UIView that overrides its drawRect method and sets anti-aliasing on for it then lets the parent draw into it. Something along the lines of:
- (void) drawRect:(CGRect)area
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGContextSetShouldAntialias(context, true);
CGContextSetAllowsAntialiasing(context, true);
[super drawRect:area];
// We have to turn it back off since it's not saved in graphic state.
CGContextSetAllowsAntialiasing(context, false);
CGContextRestoreGState(context);
}
On the other hand, it might be too late in the rendering pipeline by the time you get here, so you may have to end up rolling your own animation scheme that lets you have full control over pixel-positioning.
Per Jeeva's comment above, you can make UIView edges render with Anti-aliasing by settings the following option in the info.plist to YES.
Renders with edge antialiasing
Here is the link Jeeva pointed to:
http://www.techpaa.com/2012/06/avoiding-view-edge-antialiasing.html