Currently I am loading all of my UITableViewControllers with images and text. I'm not sure if there is a way of shortening my loading times. I'm thinking that GCD might be the best route to go, however, I'm not too sure that I'm using this correctly:
dispatch_async(dispatch_get_main_queue(), ^{
[some method];
});
This is being loaded in the ViewDidLoad, and I'm unsure if this is the correct place to use GCD. Also, is this correct way of asynchronously loading information?
use this, it downloads multiple files in different threads.And its asynchronous.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//load image here
});
If you want to load all images ,even the images which might be very down in tableView (say image at 20th row).Then viewDidLoad is perfect approach, or if you want to only load images that are currently to be shown, then download images in cellForRow:atIndexPath method.
OR,you can use AFNetworking to download multiple files much efficiently.
Hope this helps
For loading images faster in table view cells you can use SDWebImage.
It is very simple to use. Just import the SDWebImage folder into your Xcode project. It contains a category on UIImageVIew. So on any imageView object just call the method setImageWithURL as illustrated below:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *MyIdentifier = #"MyIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:MyIdentifier] autorelease];
}
// Here we use the new provided setImageWithURL: method to load the web image
[cell.imageView setImageWithURL:[NSURL URLWithString:#"http://www.domain.com/path/to/image.jpg"]
placeholderImage:[UIImage imageNamed:#"placeholder.png"]];
cell.textLabel.text = #"My Text";
return cell;
}
Related
I am making a program in which I am fetching a image from URL and displaying on the custom cell and rest of the table data on other cells.
Here is the code:
image=[[UIImage alloc]init];
if(indexPath.row==0)
{
LabelTableViewCell *cell=[detailtable dequeueReusableCellWithIdentifier:#"imagecell" forIndexPath:indexPath];
NSOperationQueue *myqueue=[[NSOperationQueue alloc] init];
NSBlockOperation *downloadOperation = [NSBlockOperation blockOperationWithBlock:^{
[cell.actindi startAnimating];
image=[UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:[frontimage objectAtIndex:0]]]];
}];
downloadOperation.completionBlock=^{
cell.imageprimary.image=image;
[cell.actindi stopAnimating];
};
[myqueue addOperation:downloadOperation];
return cell;
}
else
{
LabelTableViewCell *cell = [detailtable dequeueReusableCellWithIdentifier:#"tabledetail" forIndexPath:indexPath];
cell.labelspec.text=[firstArray objectAtIndex:indexPath.row];
cell.txtfield.text=[secondArray1 objectAtIndex:indexPath.row];
return cell;
}
return 0;
Problem I am facing:
Whenever I scroll up it reloads the top cell.
It dont reload the image on first cell till I click on that cell.
if(!cell) and if (cell==nil) not working.
Reloading the table puts in an infinite loop till I scroll down.
So I want to permanently allocate the memory to the first cell as it is only one in number.
I have tried putting nil in dequeueReusableCellWithIdentifier and its not working.
I have gone about 20-30 articles to do that but nothing has worked for me I don't know why.
So please give me any specific solution at the present condition.
I am new in the iOS developing don't know too much about it . This is my first program that I am trying to make.
You can try it different way:
To download and show image Just use library - https://github.com/rs/SDWebImage
Library will download image and cache for you.
I have a uitableview where I use a custom cell. However, when I scroll the table view there is some serious lag. It happens when I set the UIImaveView's image property with an image. I am accessing an image from the directory. But since file IO is slow I am using the dispatch_async to load the image into a UIImage object on a separate thread.
However there is still some lag. When I scroll up and down the rows without any images, the scrolling is very smooth. However when the row actually has an image, there is lag. the momentum scrolling will halt, then the app becomes unresponsive, then when the image finally loads the momentum continues where it left off.
I am not sure what is causing the lag. At first I thought it had to do with the image being too large so i tried scaling it down. Still lags. Again, if I don't set the image in the custom cell there is no lag. But when I do set it there is lag. I am not sure how to fix this.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
DHTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kReuseIdentifierGoalCell forIndexPath:indexPath];
[self configureCell:cell forIndexPath:indexPath isForOffscreenUse:NO];
return cell;
}
- (void)configureCell:(DHTableViewCell *)cell forIndexPath:(NSIndexPath *)indexPath isForOffscreenUse:(BOOL)offscreenUse {
if (cell == nil) {
return;
}
[cell setDelegate:self];
PATH_TO_FILE = SQLITE_QUERY_TO_GET_PATH; //some pseudo codes
__weak typeof(sSelf)wSelf = sSelf;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
__strong typeof(wSelf)sSelf = wSelf;
UIImage *unscaled_image = [UIImage imageWithContentsOfFile:PATH_TO_FILE];
UIImage *image = [unscaled_image imageScaledToFitInSize:kCellUIImageSize];
__weak typeof(sSelf)wSelf = sSelf;
dispatch_async(dispatch_get_main_queue(), ^{
__strong typeof(wSelf)sSelf = wSelf;
DHTableViewCell *cell = (id)[sSelf.tableView cellForRowAtIndexPath:indexPath];
if (cell) {
[cell.imageStored setImage:image]; //Commenting this out relieves all lag
}
});
});
}
#Calimari328 I hate if to put this as an answer because it does not really answer your question but the truth is what you should really do is use a library to achieve this. for example SDWebImage
Example:
#import <SDWebImage/UIImageView+WebCache.h>
...
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *MyIdentifier = #"MyIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:MyIdentifier] autorelease];
}
// Here we use the new provided setImageWithURL: method to load the web image
[cell.imageView setImageWithURL:[NSURL URLWithString:#"http://www.domain.com/path/to/image.jpg"]
placeholderImage:[UIImage imageNamed:#"placeholder.png"]];
cell.textLabel.text = #"My Text";
return cell;
}
You have to rethink this, cells get reused, that means each time you scroll your app will try to download images again.What about performance? memory ? cache ? processing?
As you think more about it is a lot more complex then a simple async task. Fortunately there are opensource projects to achieve this. No need to reinvent the wheel.
Please note I am not advertising any library if you want to write your own code you can do this as well. You can also search on the web for easy ones to implement.
I have tableviewcontroller and data fecthed from the server. I use following class to download the data asyn. but my problem is data is loading when user sees the tableViewcontroller. I want data being loaded before user sees.
#import <SDWebImage/UIImageView+WebCache.h>
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
cell.textLabel.text = [[tableData objectAtIndex:indexPath.row] valueForKey:#"name"];
cell.textLabel.font = [UIFont fontWithName:#"BebasNeue" size:24];
cell.textLabel.textColor = [UIColor whiteColor];
NSString *imageURLString=[[tableData objectAtIndex:indexPath.row] valueForKey:#"logo"];
NSString* imageURL = [[tableData objectAtIndex:indexPath.row] valueForKey:#"picture"];
[cell.imageView setImageWithURL:[NSURL URLWithString:imageURL]];
}
Solution
With SDWebImage you can download the image first.
Where?
It depends on your implementation. Maybe in a previous controller, or in your appDelegate. If the tableview appears in your initial viewcontroller, chances are to absolutely depend on the network speed.
Example Code
Take a look at this code (extracted from the Readme of the library, check the source!):
Manager *manager = [SDWebImageManager sharedManager];
[manager downloadWithURL:imageURL
options:0
progress:^(NSInteger receivedSize, NSInteger expectedSize)
{
// YOU DON'T NEED THIS
}
completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished)
{
if (image)
{
// MAYBE YOU WANT TO DO SOMETHING HERE
}
}];
Then, in your tableView:cellForRowAtIndexPath: method, you can just set the image like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// ...
NSURL* url = GET_IMAGE_URL;
[cell.imageView setImageWithURL:url];
// ...
}
How it works?
The library takes care of looking first in its cache, where it will find the previously downloaded image, because it uses the image urls as keys for the cache, so that the image appears inmediatly.
Issue
You are downloading data and loading it to the tableView async, that means your data process is executing on background, but the tableView is still active and it's shown.
My Solution
I handle this situation in two ways
You first load all the data in a NSArray or NSDictionary (whatever object you need) before presenting your TableViewController, and pass it as an argument to the TableViewController. In other words, you need to load data before presenting the tableViewController.
You need to create an animation view, or a loading view while you are performing the download of the data you tell the user that data is loading so wait until the process is done. And when the process is done, simply reload the tableView and pass the data to the TableViewCells.
Example
1) This code is used when you send request to server to get data (for example from JSON)
LoadingView *spinn = [LoadingView loadSpinnerIntoView:self.view];
dispatch_queue_t downloadQueue = dispatch_queue_create("LoadingView", NULL);
dispatch_async(downloadQueue, ^{
// do our long running process here
[NSThread sleepForTimeInterval:0.1];
// do any UI stuff on the main UI thread
dispatch_async(dispatch_get_main_queue(), ^{
//Start downloading your data, and pass data you fetched to objects of NSDictionary
//Your methods
//After downloading data is done, reload data on tableView
[tableView reloadData];
//Remove loading view, and your tableViewController is all set
[spinn removeSpinner];
});});
2) After fetching data to NSDictionary or NSArray from the request you made, you have also links to download images.
For downloading image async without blocking mainThread you can refer to #sonxurxo question, or you can use this one which also uses SDWebImage
You just need to #import <SDWebImage/UIImageView+WebCache.h> to your project, and you can define also the placeholder when image is being downloaded with just this code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *MyIdentifier = #"MyIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:MyIdentifier] autorelease];
}
// Here we use the new provided setImageWithURL: method to load the web image
[cell.imageView setImageWithURL:[NSURL URLWithString:#"http://www.domain.com/path/to/image.jpg"]
placeholderImage:[UIImage imageNamed:#"placeholder.png"]];
cell.textLabel.text = #"My Text";
return cell;
}
I have an app where I load a lot of large images. When I lazy-load them, and even after the image has been loaded, the cell does not load them until I take my finger off the screen. I am calling my downloadImageForVisiblePaths function in the UIScrollViewDelegate methods scrollViewDidEndDragging and in scrollViewDidEndDecelerating apart from this, I am also setting the image in the UITableView's cellForRowAtIndexPath method like so:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
// Code to load reusable custom cell
CustomObject *object = (CustomObject*) [self.tableArray objectAtIndex: indexPath];
if(!object.mainImage){
[self downloadImageForIndexPath:indexPath];
cell.mainImageView.image = [UIImage imageNamed:#"placeholder"];
}else{
cell.mainImageView.image = object.mainImage;
}
return cell;
}
Where the downloadImageForIndexPath looks like this:
-(void) downloadImageForIndexPath:(NSIndexPath*) indexPath{
UIImage *loadedImage = [[UIImage alloc] init];
// take url and download image with dispatch_async
// Once the load is done, the following is done
CustomCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
cell.mainImageView.image = loadedImage;
CustomObject *object = (CustomObject*) [self.tableArray objectAtIndex: indexPath];
object.mainImage = loadedImage;
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableVIew reloadData];
});
}
I can't see where I am going wrong. I need the images to load even when the finger is on the screen. This behaviour is similar to how the images load on apps like Google+, Instagram or Facebook.
Any pointers will be much appreciated.
It's hard to tell since you didn't include all the code for downloadImageForIndexPath, but it looks like you are assigning an image to a cell from a background thread (you shouldn't touch UI controls from background threads). Also, if you'r updating cell directly, you don't need to call reloadData.
I would also suggest using SDWebImage for displaying remote images in a tableview.
I am using a tableview which loads images from the documents directory, creates a thumbnail and shows it in the tableview. However, I have a problem: it becomes slow and crashes as the pictures are large, taken using the camera.
I have explored several solution including GCD to do the work in a background thread but the result is the same thing. So, I thought to look into SDWebImage but I don't know if it will also work for local files, not web images in this case. Can someone advise me please? If not, how is this problem solved? Is there an API that can help to resolve this issue?
That question is not easy to answer as the Question is asked fairly broad but I will do my best.
First, I usually dispatch a Background Thread if I have expensive processing to do as to not block the Main Thread, which is fairly important.
I don't really know why you are not using the normal UIImageView for what you are doing but try to implement following :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"YourCell";
MyCellClass *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[MyCellClass alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
/*
Whatever Code you want
*/
NSArray* params =#[cell.myImageView, #"http://myfancyimages.com/image.png"];
[self performSelectorInBackground:#selector(loadEventImageWithParameters:) withObject:params];
return cell;
}
And now add the function :
- (void) loadEventImageWithParameters:(id) parameters {
NSArray* params = [[NSArray alloc] initWithArray:(NSArray*)parameters];
NSURL *url = [NSURL URLWithString:[params objectAtIndex:0]];
UIImage *image = [UIImage imageWithData: [NSData dataWithContentsOfURL:url]];
UIImageView* theImageView = (UIImageView*) [params objectAtIndex:0];
[theImageView setImage:image];
}
If you got a lot of Pictures to load you are well advised to queue your Processes so you don;t "steal" all resources with Grand Central Dispatch.
Have a read through this excellent post http://www.raywenderlich.com/4295/multithreading-and-grand-central-dispatch-on-ios-for-beginners-tutorial for further details.
Hope that helped