UIImage stutter on first load - iphone

I have a UIScrollview that zooms a PNG image in and out on a double tap. The way I have it set up, I create a few multiple sizes of the PNG image using UIGraphicsBeginImageContext/UIGraphicsEndImageContext and store all the UIImages in an NSMutableArray. I then show the correct image on screen by swapping a UIImageView's image to the correct UIImage based on the current zoom level (I do all this to show a nicely anti-aliased image at all times rather than scaling just the original image).
The problem I have is that the very first time the image gets swapped to one that hasn't been displayed before, there is a slight stutter. After the first time, I can zoom in and out all day and there is no stutter. I have tried the solutions suggested here and here but they did not fix the problem.
Currently, I found a workaround by swapping the image after 0.01 seconds, and canceling any pending swap requests in the meantime. This works ok but it's not a solid fix. Obviously there has to be a way to get the images in a ready state since they become ready after the first time they are displayed. Please help me!

You don't actually need to create different sizes of the png. What you should have is a UIImageView inside the scrollview with the original PNG as it's image. Then add this to your .m file (make sure you have in your header file.
-(UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView{
return theImageView;
}
you can also set the max and minimum zoom scale by doing this:
[scrollview setMinimumZoomScale:0.5];//will be half size
[scrollview setMaximumZoomScale:3.0];//will be 3X
Doing the above will automatically set up the pinch zoom for you. THen you just need to code in to listen for the double tap and tell it to do this:
[scrollview setZoomScale:1.0 animated:YES];//returns it back to original size
Hope this helps - let me know if not the effect you were looking for.
Cheers,
Michael

Related

How do I blur (ios7 style) a section of an image in a UITableViewCell?

I've got a UIImageView that takes up the whole cell and shows an image. I'd like to blur the bottom third of the view in the iOS7 style (we're targeting iOS7). There will be a label over the blurred part.
The issue seems to be that I can't "screenshot" the UIImageView right as I am setting up the cell in tableView:cellForRowAtIndexPath: as it hasn't loaded yet. Although i've even tried setting up a timer for a 0.1 second delay and that also doesn't work.
I've tried the stuff on http://damir.me/posts/ios7-blurring-techniques
I've tried https://github.com/justinmfischer/7blur
I'd like to use the new screenshotting API drawViewHierarchyInRect:afterScreenUpdates:
I'd also like to use apple's sample code UIImage+ImageEffects
The only thing that has worked so far has been to just put a UIToolbar in but the performance is really slow. I don't need dynamic updates.
I've managed to get the following effect a few times but it has just tinted the view and hasn't actually blurred anything so I'm a bit confused.
Use UIImageEffects sample from apple
Make a Image View with a size of the lower portion you want to blur... and then set its image to a blurred one. also set its content mode to UIViewContentModeBottom for the proper clipping
Since your image isnt the size of the cell.. first get a temp image as it is being displayed in the cell by drawing a UIImage from it.. refer for the code here
How to capture UIView to UIImage without loss of quality on retina display
then blur that image
Load this image into a UIImage, blur the image, clip to fit the area that you want. Use UIImage+blur.h

iPhone - Knowing when an UIImageView has loaded its image

I'm loading a 5MP image into an UIImageView calling self.imagecontainer.image = myUIImage. myUIImage is an image coming from the camera of the iPhone.
This takes some time before the image can be seen on screen, and even if I can reduce this time, I need to start a process when the image is really displayed on the screen.
How may I know that the image is loaded and displayed, and not just still being processed by the UIImageView ?
Well... first thing is you shouldn't be setting a 5MP image in an imageView. An imageView is meant for on screen display only and even though it scales the image you set for display purposes the original is retained so your memory footprint goes way up. If you do this at best you will have a poor performing app that deals with lots of memory warnings. At worst, you'll crash often.
So, you should resize your image to the smallest size that meets your onscreen display needs and then set that image within your imageView. This is my favorite blog post on how to resize images: http://vocaro.com/trevor/blog/2009/10/12/resize-a-uiimage-the-right-way.
Now, to your question. You can't know for sure when the image is displayed but it will happen so quickly once you scaled you can just assume it is displayed as soon as you set it. So do your scaling on a background thread. When the scaling is done, send a message to the main thread to set the scaled image in the imageView and then do whatever you want.

CATiledLayer blanking tiles before drawing contents

All,
I'm having trouble getting behavior that I want from CATiledLayer. Is there a way that I can trigger the tiles to redraw without having the side-effect that their areas are cleared to white first? I've already subclassed CATiledLayer to set fadeDuration to return 0.
To be more specific, here are the details of what I'm seeing and what I'm trying to achieve:
I have a UIScrollView with a big content size...~12000x800. Its content view is a UIView backed by a CATiledLayer.
The UIView is rendered with a lot of custom-drawn lines
Everything works fine, but the contents of the UIView sometimes change. When that happens, I'd like to redraw the tiles as seamlessly as possible. When I use setNeedsDisplay on the view, the tiles redraw but they are first cleared to white and there's a fraction-of-a-second delay before the new content is drawn. I've already subclassed CATiledLayer so that fadeDuration is set to 0.
The behavior that I want seems like it should be possible...when you zoom in on the scrollview and the content gets redrawn at a higher resolution, there's no blanking before the redraw; the new content is drawn right on top of the old one. That's what I'm looking for.
Thanks; I appreciate your ideas.
Update:
Just to follow up - I realized that the tiles weren't being cleared to white before the redraw, they're being taken out entirely; the white that I was seeing is the color of the view that's beneath my CATiledLayer-backed view.
As a quick hack/fix, I put a UIImageView beneath the UIScrollView, and before triggering a redraw of the CATiledLayer-backed view I render its visible section into the UIImageView and let it show. This smooths out the redraw significantly.
If anyone has a better solution, like keeping the redraw-targeted tiles from going away before being redrawn in the first place, I'd still love to hear it.
I've found that if you set levelsOfDetailBias and levelsOfDetail both to the same value (2 in my case), then it only redraws the tiles that are touched by my setNeedsDisplayInRect: call, as you'd hope.
However if the levelsOfDetail is different to LODB, then any calls to setNeedsDisplayInRect: redraw all the tiles.
You could add another layer (possibly a CATiledLayer) behind the existing tiled layer. (Sort of a double-buffered solution.) You would call setNeedsDisplay: on the second layer from a timer that fires after a few seconds to ensure that that layer doesn't redraw at the same time as the front layer.
Another potential option is to use the same delegate to draw content to a bitmap context and swap the bitmap into the backing store once the content is refreshed. This should produce a flicker-free result. That being said, I can't tell you how this might be done, and one nice thing about CATiledLayers is they automatically generate tiles when you zoom and pregenerate tiles when you pan once zoomed in.
I would like to see how you implement your application. I have been looking for weeks to find an example that uses a combination of UIScrollView and a CATiledLayer-back view with a lot of custom drawn lines. Apple has some great sample code - but it all involves images rather than line art, so no help for me.
Having read through these answers without a solution, I discovered that tiling a page was the dominant background task.
Preparing my lo-res placeholder image on a high priority queue solved this issue - the images now appear while the tiling is occurring. Caching the placeholder images further improves their appearance - they appear before the tiling begins.
With newer devices, the tiling it so fast, these tricks might not matter. A sample PDF consisting of large scanned images (e.g. a scanned book) tiles the slowest in my experience, and makes for good test data.
I had the same problem with iPad.
The solution was more simple than I thought and far more simple than using UIImageView to render display before redrawing... :
Just don't set any background color for the Layer!
I had CATiledLayer set in a similar way:
layer = [[CATiledLayer alloc] init];
layer.masksToBounds = YES;
layer.contentsGravity = kCAGravityLeft;
//layer.backgroundColor = [[UIColor whiteColor] CGColor];
layer.tileSize = CGSizeMake(1004.0, 1004.0);
layer.levelsOfDetail = 16;
layer.levelsOfDetailBias = 8;
Note that I have commented out the line setting layer's background color to white.
After that the white blank before redraw problem disappeared!
Let me know if anyone has tried that.

iPhone - increase UIImageView size and keep the image size fixed

I have a custom UIImageView class which I use to handle multi-touch events on the UIImageView. When the user touch begins, I want to increase the UIImageView's frame but keep the UIImage size fixed.
I tried changing the UIImageView's frame and then calling the drawInRect: method of UIImage to keep the UIImage's size fixed. But this is not working.
The contentMode for the UIImageView is set as ScaleAspectFit and as soon as I increase the frame size of the UIImageView, the UIImage size also increases (and is not affected by the drawInRect:)
Can someone please tell me how I can achieve this?
Thanks.
Adding more details
What I am trying to do is this
Place a UIImageView on the screen with the size same as the size of the image
When the user selects the image, anywhere he touches, the image edits as if the user is doing multi-touch with the image
If I increase the size of the imageview to detect touches any where, the image size also increases... Hope that makes things clearer!
Thanks
There may well be other ways to do it, but I think the UIImageView is doing what it's intended to do here.
What do you want the area of the view not covered by the image to look like? Be transparent? Have a solid colour?
Why do you want to do this? Is it to capture touch events from a wider area than that under the image itself?
If you have a good reason for needing to do this I would create a new view, probably just a plain UIView (with background set to transparent colour), and add the UIImageView to that. Make the plain view the one you resize.
I haven't specifically tried this, but I think it would work:
imageView.contentMode = UIViewContentModeCenter;

How to dynamically restore the UIScrollView's zoom level and position when my app relaunches?

My application consists of a UIImageView inside a UIScrollView, and I display a big image inside of it. The scroll view allows the user to pinch to zoom in/out in the image, and that all seems to work just fine.
However, when my application is terminated and then re-launched, the UIScrollView displays the image again in the original zoom level (which is currently set to display the whole image, by scaling it in a "aspect fit" mode).
I would really like to be able to re-launch my app and have the UIScrollView reopen with the same parameters as it was set when the app terminated. So if my image is currently zoomed in to the max, and scrolled all the way to the bottom left of it, that should be the view when I open the app again.
How can I do this?
Check the .transform property of the view. If you are zoomed in, this should be modified. Save and restore it on the next launch.
I have found a way to programmatically zoom UIScrollView. This may help you set the desired zoom level upon startup.
The sample code, together with ZoomScrollView class that encapsulates the required zooming magic and a detailed discussion of how (and why) UIScrollView zooming works is available at github.com/andreyvit/ScrollingMadness/.
Someone asked this a while ago, but I don't think you'll like the answer. Hopefully you'll get a better one.
I do it like this:
When you quit save the zoomlevel (myScrollview.zoomScale) and the contentview frame origin.
When you reopen you can set the zoomscale. You also have to set the content size to the new zoomlevel, because otherwise the boundaries are not correct.
[myScrollview setZoomScale:savedZoomScale];
CGSize newsize = CGSizeMake(origsize.width * savedZoomScale,
origsize.height * savedZoomScale);
myScrollview.contentSize = newsize;
To set the offset:
[myScrollView setContentOffset:savedFrameOrigin animated:YES];