Best practice for preloading and caching images (AFNetworking) - ios

I was wondering what is the best practice for this very common action:
Say i have a collection view, and each cell of this collection view has an imageView that receives a url for an image.
The images are large, so i want to preload all the images in advance (or say 5 in advance).
I'm using AFNetworking imageView Category.
So here's what i did - creating a temp UIImageView and set the url to it each iteration, but it seems not to be working right. (the images still takes time to show up, while i want it to be instantly, and if i close the internet connection, the images do not load from cache).
- (void)preloadGalleryImages {
for (GalleryItem *item in _galleryItems) {
UIImageView *tempImageView = [[UIImageView alloc] init];
[tempImageView setImageWithURL:item.imageURL placeholderImage:IMAGE(#"placegolder")];
}
}
}
Thanks,

There is a universal way of preheating images in collection views that was implemented in one of the Apple PhotosKit samples. You can anticipate user actions and fetch images that might soon appear on the display (that are close to the viewport). This feature is implemented in Nuke and DFImageManager.

Related

Animated Background

I am making an app in which there are two buttons in the center. The background is there and I need the background to have an animation. With that said I mean like little raindrops constantly falling in the background. I have no clue on how to do this. My customer really, really wants this. Thanks!
Make sure the images are named in numerical order with the number at the end of the name. Then drag and drop the file of images into your Xcode folder area. Then you reference only the image name and not the number in animatedImageNamed.
You need to create an ImageView the size of the Background for each device you plan on using.
//place this in your viewDidLoad
UIImageView * background = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 320, 55)];
UIImage * image = [UIImage animatedImageNamed:#"MovingCar" duration:3.6];
background.image = image;
[self.view setBackgroundView: background];
You can change the duration to suit your needs accordingly.
You could use the UIView method animateWithDuration:animations: or one of it's variants that has a completion method to animate images from the top to the bottom of the screen. Take a look at the Xcode docs on that method, and search the web for examples.
As the other poster says, this site is for helping people with problems, not for asking others to provide you with ready-made solutions.
I could bang out some animation code for you that would create an endless stream of falling raindrops, but it would probably take me a couple of hours to get it fully working, and I'd have to charge you for it. (Plus I'm not very familiar with Swift so I'd have to do some translation from Objective-C, which is my day-to-day programming language.)

Pinch zooming to multiple images in view controller

I am new in Ios & learning It.
I am creating application based on images.
I have multiple images in my viewcontroller, Which i had passed throgh another view controller(Named as a View2) via Nsuser default as below.
NSString *str11 = [[NSUserDefaults standardUserDefaults]stringForKey:#"Vishal"];
NSLog(#"%#",str11);
if(![str11 isEqualToString:#""])
{
img12 = [[UIImageView alloc]initWithImage:[UIImage imageNamed:str11]];
img12.frame = CGRectMake(0, 0,100,100);
[secondview addSubview:img12];
[self.view addSubview:secondview];
Here In key "Vishal" I have bunch of 5 images.
Which i passed to Uiimageview *img12 in my view controller.
which i had push to my view controller.
I want to zoom these images.
Now I want to assign pinch zooming to all my images.
I had listen like You have to provide another view for Zooming.
now i am stuck at how to assign view to every images which are comes from another viewcontroller.
And how i knew which image user is tryng to pinch zooming.
Please Help me.
If you are dealing with multiple images, you will need to have a separate view for this purpose. I am providing all the steps:
Steps:
You need to create a separate view.
Add scrollview on is nibt.
Add an imageview on scroll view.
Add all zoom delegate methods, for zoom feature, in this class only like viewForZoomingInScrollView etc.
In viewForZoomingInScrollView return this imageview.
Now where you were adding images
In you image array loop create separate instance of this new class.(Suppose you have five images you will have to create five instances)
Add those images to this new class's imageview.
Now you are able to add zoom separately on every images without bothering how many images you have.
With this approach you can optimize your code as well. Suppose you will have more than 10 images , thet you can load only two instances of this class and reuse those view turn by turn according to your need, to show the images when user swipes.

Memory not released after removing UIImageView from superview

On iOS 6.1, I have a view controller with a scrollView in which I need to display some images as the user flicks through the pages. The scrollView has 3 subviews to display the current, the previous and the next image respectively. Images are substituted during the scrolling.
The mechanism works fine, but resources are not released as expected and the app is terminated after scrolling approximately 15 images.
At first I tried solving the problem simply assigning the new image to the UIImageView, but it did not work (I read that images are cached), so I tried a different approach:
// access the UIImageView of the currentView
for (UIView *subviewOfCurrentView in [currentView subviews]) {
if ([subviewOfCurrentView isKindOfClass:[UIImageView class]]) {
UIImageView *cv = (UIImageView *)subviewOfCurrentView;
//cv.image = nil;
[cv removeFromSuperview];
UIImageView *new_cv = [[UIImageView alloc] initWithFrame:photoRect];
new_cv.image = [UIImage imageNamed:[myObject getFileName];
[currentView addSubview:new_cv];
}
}
Profiling the app with the Activity Monitor shows that the Real Memory Usage keeps growing when I flick an image, despite the fact that the number of subviews remains constant.
Note that the app is terminated without calling didReceiveMemoryWarning.
How can I force my app to release the memory used by UIImage(s) and UIImageView(s)?
If you can help me, much appreciated.
UIImage loaded by imageNamed: are are not automatically released (they are cached and images with the same name share the same data).
Use imageWithContentsOfFile: or imageWithData:scale: if you want the image to be automatically released. Note that you can get the image path using methods from NSBundle although it will be a bit more complicated.
You will have to implement the following part by yourself
If the screen has a scale of 2.0, this method first searches for an image file with the same filename with an #2x suffix appended to it. For example, if the file’s name is button, it first searches for button#2x. If it finds a 2x, it loads that image and sets the scale property of the returned UIImage object to 2.0. Otherwise, it loads the unmodified filename and sets the scale property to 1.0.
Not sure how to get path for an image when using the new iOS 7 image stores.

Place two UIImages in one UIScrollView

I was tasked to create a image viewer just like IKEA's catalogue viewer. It's completely out of my skills but after all I must try it.
I was once able to create photo viewer with UIScrollView like iPhone's Photo App, but IKEA's catalogue app is far more complicated as it has different parts per catalogue (title page, spread pages, back cover page). It cannot be done by my photo viewer code since it only requires simple array of UIImages. I have to separate them in several parts.
To achieve that, first I have to look for the way to put two UIImages into one UIScrollView, to make those two pages as one spread (two facing pages) zoomable UIScrollView. And our "web service" provides just an array of pages...
Is there any way to achieve this? I have searched around but all of them are the case about "marging" two UIImages, not putting them side by side as one UIImage. Should I have to create one UIImage from two UIImages then put it to the UIScrollView?
I know it's rather simple and noob question, but I really don't know where to start.
Any help would be appreciated.
If you want to make photo viewer then you must refere SDWebImage. This will cache UIIamge with lazy loading. Use it's sample code. It will help you a lot.
All the best !!!
To nest UIImages in the UIScrollView, you should create 2 UIImageViews, figure out the frame dimensions you need, then call [self.scrollView addSubview:myImageView]; for each UIImageView. For example:
UIImage *image1 = [UIImage imageNamed:#"image1"];
UIImage *image2 = [UIImage imageNamed:#"image2"];
UIImageView *imageView1 = [[UIImageView alloc] initWithImage:image1];
// put your specific frame values here
imageView1.frame = CGRectMake(100.0f,100.0f,200.0f,200.0f);
UIImageView *imageView2 = [[UIImageView alloc] initWithImage:image2];
// put your specific frame values here
imageView2.frame = CGRectMake(300.0f,100.0f,200.0f,200.0f);
// This is a property tied to your scrollView, either created in code or in Interface Builder
[self.scrollView addSubview:imageView1];
[self.scrollView addSubview:imageView2];

Lazy Loading of several images in UITableView

What are the best practices to download several images and load each of them in a UIImageView which is inside a UITableViewCell? Particularly, should I resize/replace the UIImageview after downloading or should I resize the image to fit into the UIImageView. Note that resizing/replacing UIImageView also resize/replace UITableViewCell. Will it cause any issue?
A couple of thoughts:
What are the best practices to download several images and load each of them in a UIImageView which is inside a UITableViewCell?
The best practices regarding the downloading of images include:
Definitely avail yourself of lazy loading (load images as you need them and not before).
Download images asynchronously.
Make sure your download technique will cancel requests for tableview cells that are no longer visible. For example, if you're on a slow network and scroll down quickly on your table view, you don't want to tie up your device downloading images that aren't visible anymore.
Make sure you don't make too many concurrent requests of your server. In iOS, when you exceed 5 or 6 concurrent requests, subsequent requests will freeze until the prior ones complete. In worst case scenarios, the subsequent requests will actually start failing as they timeout.
Cache your results. At the very least, cache them in memory. You might also want to cache them to persistent storage (a.k.a. "disk"), too.
If you were going to write your own code for the asynchronous operations, caching, etc. you might want to use NSOperationQueue instead of GCD so that I could constrain number of background requests and make the requests cancelable. You would use NSCache to cache the images. And you'd probably use a UITableViewCell subclass (or a category) so that you can save weak reference to "previous" operation, so that you can cancel any incomplete, prior requests.
As you can see, this is non-trivial, and I'd suggest you using an existing UIImageView category, such as those available as part of SDWebImage or AFNetworking. IMHO, the former is a little richer (e.g. offers disk caching), but if you're doing a lot of networking and want to stick with a single framework, AFNetworking does a great job, too.
Later you ask:
Particularly, should I resize/replace the UIImageview after downloading or should I resize the image to fit into the UIImageView. Note that resizing/replacing UIImageView also resize/replace UITableViewCell. Will it cause any issue?
If your images are larger than what your cell's thumbnail view requires, you have two approaches. First, you can use a contentMode of UIViewContentModeScaleAspectFit or UIViewContentModeScaleAspectFill (and if you use AspectFill, make sure you also set clipsToBounds to YES). Even better, you can actually resize the image after you download it.
It's personal opinion, but I think it's a better UX to have a UIImageView of fixed size on the cell and then when the asynchronous image download is done, just set the image property of the UIImageView. You want the images to gracefully appear in your UI as they're downloaded, but you generally don't want a jarring re-layout of the view while the user is already in the process of reading what's there. If your design absolutely necessitates the re-layout of the cell, then you can just call reloadRowsAtIndexPaths.
A common practice for lazy-loading images in a UITableViewCell is to use a notification callback to let the UITableViewCell know when the image has been received.
In essence, you'll want to create a subclass of UIImageView that has an imageURL field that, when changed, fires off a request for the image, and use that instead of a standard UIImageView:
Interface for the UIImageView subclass:
#property (nonatomic, copy) NSString *imageURL;
Implementation for the UIImageView subclass:
//synthesize property
#synthesize imageURL = _imageURL;
- (void)setImageURL:(NSString *)imageURL {
if(_imageURL)
[[NSNotificationCenter defaultCenter] removeObserver:self name:_imageURL object:nil];
_imageURL = [imageURL copy];
//if imageURL is valid...
if(_imageURL.length) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(didReceiveImage:) name:_imageURL object:nil];
//fire off some asynchronous image fetch
//when the image fetch completes, sent off a notification using the imageURL as the notification name
//It's up to you to create the implementation for this yourself
... [MyImageManager fetchImage:_imageURL notificationName:_imageURL];
}
}
- (void)didReceiveImage:(NSNotification*)notification
{
//handle your received image here
if([notification.object isKindOfClass:[UIImage class]])
{
self.myCustomImageView.image = notification.object;
}
}
And then in the UITableViewCell class when you override prepareForReuse:
- (void)prepareForReuse {
[super prepareForReuse];
self.myCustomImageView.imageURL = nil;
self.myCustomImageView.image = nil;
//do the rest of your prepareForReuse here:
...
}
Now, as far as the whole re-sizing of the images vs resizing of the imageview, what you ought to do is leave the imageView's size alone, and use the contentMode property to handle the varying sizes for the resulting images. Your possible values are:
UIViewContentModeScaleToFill,
UIViewContentModeScaleAspectFit,
UIViewContentModeScaleAspectFill,
UIViewContentModeRedraw,
UIViewContentModeCenter,
UIViewContentModeTop,
UIViewContentModeBottom,
UIViewContentModeLeft,
UIViewContentModeRight,
UIViewContentModeTopLeft,
UIViewContentModeTopRight,
UIViewContentModeBottomLeft,
UIViewContentModeBottomRight,
Each of which has their own respective result - you will probably want to use UIViewContentModeScaleAspectFit as this will re-size the image to fit the imageView, without distorting it - this may leave empty margins to the size or top/bottom of the imageView. Aspect fill will do a similar thing, only it will resize the image to be large enough to fill the entire imageView, and may cut off the sides or top/bottom of the image. Scale to fill will just stretch the image to fill the imageView.

Resources