I've got magazine app and I want to know If is there any way how to preload UIView and UIImages?
My views structure:
MagazineView
-> pageView
-> imageContainerView
-> image
-> imageContainerView
-> image
-> textView
-> pageView
etc...
So my question is - How to preload images before views are visible? I want to make some Cache with 3 or 5 pages and makes threads for loading views. Any ideas?
I presume that to 'preload images' means downloading them before they are actually used so there is no delay when displaying them.
What you could do is download the images first using [NSData dataWithContentsOfURL:], then either dump the data into a file using writeToFile:atomically: on your NSData instance, or simply keep the data in heap in the NSData for future reuse.
Then when you need it, you can create UIImage using either [UIImage imageWithContentsOfFile] or [UIImage imageWithData:].
Note that if you save the cache to files, save those files in the cache folder, not in the documents folder, or mark them with the attribute that makes them skipped when syncing to iCloud, otherwise your app will be rejected by Apple.
Also keep in mind that downloading those images can taken long, timeout, or fail. You should do all this in a background thread in a way that does not block the main thread, have a visual indicator that the application is not frozen and have a fallback for the case where the downloads fail or the cache is flushed (if you save the images to files)
After you load images as J_D described, just put them into pageView and set them Hidden to YES. Every time you want to switch page, just call some method, that will pre-load new images and show those, which are already downloaded (set Hidden to NO) ;-).
Hope this will help you to solve your problem...
Related
I know this is a very recurring topic but, i tried all the suggested solutions they seem not to work...
The scenario:
I load an empty view and i add at run time an UIWebview.It is a global variable,initiated only once.
the initial memory usage (by Xcode 5.1) is ~6MBytes.
i will load in the view a series of url using a button event.Each time is pressed a new page will be loaded.
i load in the webView the first page from internet with few image and one video.
After the loadRequest is completed, the memory is ~20Mbytes.
Every time i load a new page the memory increases.
The memory stays the same even when i load a new controller...
What I tried so far:
To use the [NSURLCache sharedURLCache] and related functions to instantiate a fixed amount of cache in the Application delegate: the App does not care...memory changes despite the limit of 32MBytes i set.
To set to nil the global variable: no effect.
To use temporary UIWebview(s) instead of the global one; views are removed from the superview each time the controller changes: no effect.
None of this tentatives have been successful...the memory increase and it is never released.
If i start the video from the web browser and then i load a new controller, the sound of the video still runs in the background...
What am I missing???
Thank you in advance
I have my own image downloader class, it holds a queue and downloads images one (or a certain amount) at a time, writes them to the cache folder and retrieves them from the cache folder when necessary. I also have a UIImageView subclass to which I can pass a URL, through the image downloader class it will look if the image already exists on the device and show it if it does, or download and show it after it finished.
After an image finishes downloading I do the following. I create a UIImage from the downloaded NSData, save the downloaded NSData to disk and return the UIImage.
// This is executed in a background thread
downloadedImage = [UIImage imageWithData:downloadedData];
BOOL saved = [fileManager createFileAtPath:filePath contents:downloadedData attributes:attributes];
// Send downloadedImage to the main thread and do something with it
To retrieve an existing image I do this.
// This is executed in a background thread
if ([fileManager fileExistsAtPath:filePath])
{
NSData* imageData = [fileManager contentsAtPath:filePath];
retrievedImage = [UIImage imageWithData:imageData];
// Send retrievedImage to the main thread and do something with it
}
As you can see, I always create a UIImage directly from the downloaded NSData, I never create NSData using UIImagePNGRepresentation so the image never gets compressed. When you create a UIImage from compressed NSData, UIImage will decompress it right before rendering on the main thread and thus block the UI. Since I'm now having a UITableView with a ton of small images in it that have to be downloaded or retrieved from disk, this would be unacceptable as it would slow down my scrolling immensely.
Now my problem. The user is also able to select a photo from the camera roll, save it and it also has to appear in my UITableView. But I can't seem to find a way to turn the UIImage from the camera roll into NSData without using UIImagePNGRepresentation. So here's my question.
How can I convert a UIImage into uncompressed NSData so I can convert it back to a UIImage later using imageWithData so that it doesn't have to be decompressed before rendering?
or
Is there any way I can do the decompression before sending the UIImage to the main thread and cache it so it only has to be decompressed once?
Thanks in advance.
How can I convert a UIImage into uncompressed NSData so I can convert it back to a UIImage later using imageWithData so that it doesn't have to be decompressed before rendering?
What you're really asking here, I take it, is how to store the UIImage on disk in such a way that you can later read the UIImage from disk as fast as possible. You don't really care whether it is stored as NSData; you just want to be able to read it quickly. I suggest you use the ImageIO framework. Save by way of an image destination and fetch later by way of an image source.
http://developer.apple.com/library/ios/#documentation/GraphicsImaging/Conceptual/ImageIOGuide/ikpg_dest/ikpg_dest.html
Is there any way I can do the decompression before sending the UIImage to the main thread and cache it so it only has to be decompressed once?
Yes, good question. That was going to be my second suggestion: use threading. This is what people have to do with tables all the time. When the table asks for the image, you either have the image already or you don't. If you don't, you supply a filler image and, in the background, fetch the real image. When the real image is ready, you have arranged to get a notification. Back on the main thread, you tell the table view to ask for the data for that row again; this time you've got the image and you supply it. The user will thus see a slight delay before the image appears. I'm sure you've seen lots of apps that behave this way (New York Times is a good example).
I have one further suggestion, and it may be the best of all. You speak of it taking time to decompress the image from disk. But this should take no time at all if the image is small. But the image should be small, because it's going to go into a small place - a table cell. In other words, you should shrink the images beforehand, when you first receive them, so that you are ready with the small version of each image when asked. It is a huge waste of time and memory to supply a large image that is to go into a small space.
ADDED LATER: Of course you do understand that a lot of this worry would be unnecessary if you weren't saving the images to disk. I'm not at all clear on why you need to do that. I hope you have a good reason for it; but it's a heck of a lot faster, obviously, if you just hold the images ready in memory.
I found solution:
CGImageRef downloadedImageRef = downloadedImage.CGImage;
CGDataProviderRef provider = CGImageGetDataProvider(downloadedImageRef);
NSData *data = CFBridgingRelease(CGDataProviderCopyData(provider));
// Then you can save the data
IF you download the data and save it to disk, then the data is compressed in either PNG, JPEG, or GIF format. You are not going to be downloading uncompressed image data. So, the root of your question about doing the decompression first needs to be addressed before you save the file to disk. Decompressing before you save will make the file a lot bigger, but it means that decompression is not needed before the data is read back into a CGImageRef or UIImage. It is the loading and then decompressing a bunch of images that is slowing down your CPU and making scrolling slow. But, it is not a solution to simply hold everything in memory already decompressed, because that will use up all your app memory and crash your phone before long. You might be able to get away with it for some small number of images, but this is a basic design flaw that you need to address when first writing your code. If you like, you can have a look at my blog post on this topic video-and-memory-usage-on-ios-devices, the post deals with video, but you have the exact same issue when dealing with lots of different images. I would suggest that you write your small images to disk in an uncompressed format like TIFF or BMP, that way reading them back in is easy as long as ImageIO supports that specific format.
I have some code in which I have declared 14 images to be displayed in UIImageview and named them as 1,2,3,....14.jpg. These images are in a folder copied from desktop and placed in the project.
Now I have a server and client code running from which the images named 1,2,.... will be refreshed and new images will be coming. But when I try to execute my code can see the images which came for the first time and for every repetition there will be no change in images shown in simulator....
NSArray *array1=[NSArray arrayWithObjects:[UIImage imageNamed:#"1.jpg"],[UIImage imageNamed:#"2.jpg"],[UIImage imageNamed:#"3.jpg"],[UIImage imageNamed:#"4.jpg"],[UIImage imageNamed:#"5.jpg"],[UIImage imageNamed:#"6.jpg"],[UIImage imageNamed:#"7.jpg"],[UIImage imageNamed:#"8.jpg"],[UIImage imageNamed:#"9.jpg"],[UIImage imageNamed:#"10.jpg"],[UIImage imageNamed:#"11.jpg"],[UIImage imageNamed:#"12.jpg"],[UIImage imageNamed:#"13.jpg"],[UIImage imageNamed:#"14.jpg"],nil];
[UIImageView beginAnimations:Nil context:NULL];
image1.animationImages=array1;
image1.contentMode=UIViewContentModeScaleAspectFill;
image1.animationDuration=10;
image1.animationRepeatCount=10;
[image1 startAnimating];
When this code runs I am getting the same set of 14 images which are shown for the first time. It's not getting changed even though the images are changed in that folder.
The documentation for imageNamed: says it looks in the application’s main bundle. Since you can't write to the application bundle on a device, you're not going to be able to replace things that are distributed with the app if you load them that way.
You may want to switch to imageWithContentsOfFile: and move your distributed images to a writable location when the app is first run so that you can update them.
(You don't say where you're storing the images that you get from your server.)
I want to load multiple images when my app starts from a website (i.e. all images in http://hello.com/images/ which are named 1.png, 2.png, 3.png..) , so that the images can be used anywhere in the program without needing to reload them every time I want to access them.
Can I simply create a class that holds a static NSArray and fill it at the beginning, to then create an instance of this class whenever I need the images or is there a better way to do it?
Right now, I am loading the images with the following code:
UIImage *image =[[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:[NSString stringWithFormat:#"http://hello.com/images/%#.png,item]]]];
I want to make the app as efficient as possible, so I am concerned about the creation of multiple objects making it very demanding.
Thanks
You can try downloading the images asynchronously in a separate thread when the application starts
and use it later.
Here is the SO question and answer where the poster uses a custom class to download the images in the background asynchronously.
Try this for efficient download of images and UI also will not be blocked.
I'm loading a very big file in an NSimage with this code :
[[NSImage alloc] initWithContentsOfFile:aFile]
This operation take a few time. I want to display the loading status on my UI.
How can it's possible to read or calculate a progression ?
Thanks.
Initialize the image by referencing the file, which will not load it immediately. Then, set yourself as the image's delegate and respond to the incremental-loading messages that are part of the NSImageDelegate protocol. Then, attempt to ask the image for some information about itself (asking for its representations would probably be a good way), to cause the image to start loading.
I think this will still block your UI, though: You'll be able to display progress, but not to enable the user to work on other things while the image loads. I'm not sure how you would do that.