How To Move Download of Images to Background Thread To Smooth UI? - iphone

I have this method and I was advised to do the download of images on the background thread. Can anyone help me with this?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSURL *myURL=[NSURL URLWithString:[self.picturesArray objectAtIndex:indexPath.row]];
NSData *myData1 = [[NSData alloc] initWithContentsOfURL:myURL];
UIImage *myImage = [[UIImage alloc] initWithData:myData1];
cell.imageView.image = myImage;
return cell;
}

Setting aside the fact that the code shown couldn't possibly work by itself... what you want to do is pretty easy. Your cell could be a custom UITableCell in which you define a method that does your download in the background. In cellForRowAtIndexPath:, you'd call that method, e.g. [cell loadImageInBackground:myURL]. The clever part is that the cell, of course, knows what is in it, i.e. the UIImageView you want to setup. So the background load, when it completes, can just set the image, and your table remains responsive.

You could use a UIImageView that downloads asynchronously. Check this one out:
http://iphone-dev-tips.alterplay.com/2009/10/asynchronous-uiimage.html
You should create a UITableViewCell subclass and add an AsynchronousImageView to it and then do [cell.asyncImageView loadImageFromURLString:[self.picturesArray objectAtIndex:indexPath.row]].

Related

How to set the image clarity?

i am new i phone developer i am struggle with one problem i have use images in my app that images captured through web services . My problem is each images placed on each table cell that way i was compressed that image size in my cell size but image clarity was missing. please tell me how to set the image in cell with clarity.
i am very suffer this problem please give me any suggestion for me.
hi i am working with Twitter, i got problem with images, i have tweet view have UITableview, it contains all tweet with user photo, if i load those photos in each cell when i scroll the UITableview, it scrolling very very slowly please suggest me to reduce the memory size of photos and scroll the UITableView fast.
i heard the thumbnail can reduce the memory size, does it.(if not which method i have to choose and what thumbnail method do's) if so how to do that in this code (table view cell code)
i heard the thumbnail can reduce the memory size, does it.(if not which method i have to choose and what thumbnail method do's) if so how to do that in this code (table view cell code)
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *identifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if(!cell) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewStyleGrouped reuseIdentifier:nil] autorelease];
UIImageView *myImage = [[UIImageView alloc]initWithFrame:CGRectMake(6,10,58,60)] ;
NSURL *url = [NSURL URLWithString:[(Tweet*)[tweetArray objectAtIndex:indexPath.row] image_url]]; //here Tweet is other class and image_url is method in the Tweet class
NSData *data = [[NSData alloc] initWithContentsOfURL:url];
[myImage setImage: [UIImage imageWithData:data]];
[cell.contentView addSubview:myImage];
[myImage release];
}
return cell;
}
Thanks in advance.
By using the Lazyloading concept you can make your tableview scroll fastly.
Just download this class for lazyloading.
It is very easy to impelement.
#import "UIImageView+WebCache.h" // Import this
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
------------------
------------------
[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;
}
Then the picture clarity problem: Try to use the lower resolution images(50x50) like that

one image or thumbnail for cells in a table view

i have the following code which will displays result in a UItable view along with an image.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//create a cell
UITableViewCell *cell = [[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:#"cell"];
// fill it with contnets
NSDictionary *exercise = [exercises objectAtIndex:indexPath.row];
cell.textLabel.text = [exercise valueForKey:#"player"];
UIImage *image = [UIImage imageNamed:#"iphone.gif"];
cell.imageView.image = image;
cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
// return it
return cell;
}
is there an option where i can display one image for all the cells for ex. 1 image on the left side and then 3 rows by the right side. I am a new bid and still getting my grip on iPhone coding.Please suggest me how we can do this.Thanks.
Yup, a UITableViewCell is pretty much another UIView, so you can add subviews to it and customize it anyway you need. For example, if you need to add an image to all the cells, just add it onto the contentView;
[cell.contentView addSubview:myImageView];
If you have several customizations needed for your cell, and are looking for a highly custom look as opposed to the generic look provided by the standard cells, I'd recommend looking into creating a custom UITableViewCell. The reason is that the standard cells have already laid out UI's with labels, images etc, and anything you add onto it may interfere with the existing UI in ways you do not intend.

How to place the image for every cell in tableview?

iam developing one application.In that i place the imageview in every tableview cell like
-(UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:CellIdentifier];
itemimageview=[[UIImageView alloc]initWithFrame:CGRectMake(5, 8, 75, 70)];
itemimageview.image=[UIImage imageNamed:#"thumb_mdpi.png"];
itemimageview.userInteractionEnabled = YES;
[cell.contentView addSubview:itemimageview];
return cell;
}
And when u click any row we get the image from our iPhone camera.For that i write the below code in didSelectROw method.
UIImagePickerController *imagePicker = [[[UIImagePickerController alloc] init]
autorelease];
imagePicker.sourceType=UIImagePickerControllerSourceTypeCamera;
//imagePicker.allowsImageEditing = NO;
imagePicker.delegate = self;
[self presentModalViewController:imagePicker animated:YES];
After i use the UIIMagePickerController delegate method like
- (void)imagePickerController:(UIImagePickerController *)picker
didFinishPickingMediaWithInfo:(NSDictionary *)info {
UIImage* image = [info objectForKey:UIImagePickerControllerOriginalImage];
NSLog(#"image selected");
itemimageview.image =image;
[self dismissModalViewControllerAnimated:YES];
}
But my problem is after selecting the image,that image will be appear at last cell only.If u select first row and take the image then that image will be appear at last row.So please tell me what are the changes in my code for getting the image for every row.
You can achieve this by following these steps:
1.First when the user clicks a cell, store the row number (indexPath.row) in an integer variable.
2.Once you have acquired the image from the camera,store it in a UIImage.
3.Now you know the row to be updated and you have ur image.Call reloadData method of the UITableView at the end of the didFinishPickingMediaWithInfo delegate method.
4.The reloadData method will subsequently call the cellForRowAtIndexPath method. In that have a condition like this:
if(indexPath.row==clickedRow){
//Create the imageView here and then assign the image obtained from the camera to it.
imageView.image=cameraImage;
[cell.contentView addSubView:imageView];
}
where clickedRow is the integer variable which holds the row number clicked by the user in didSelectRowAtIndexPath method.
Hope this helps.
The problem is that you are setting itemimageview to the image view inside the most recent cell the tableview grabbed from the delegate method each time a cell is drawn so itemimageview will contain the last image that the tableview drew.
In order to fix it you will have to make sure to update the imageview of the cell that was actually clicked. I think the easiest way to do this would be to use use Apple's default cell imageview with cell.image. Or you could make a subclass of UITableViewCell that keeps track of and draws the UIImageView. Then you change the image in that cell that is selected.
Either way you will need to keep track of which cell was selected in that class. One way could be to put UITableViewCell selectedCell; in the .h. then selectedCell = [self cellForRowAtIndexPath:indexPath]; in your didSelect method followed by selectedCell.image = image in your image delegate.
Sorry if this is hard to follow, it's a short answer to a moderately complex problem. There are many ways to tackle this.

Using cached images in UItableView

using iOS 4.1 SDK. I am using 2 small images in each row of a UITableView. I wanted to know which of the following 2 methods was better, also is Method 1 valid at all?
- (void)viewDidLoad
{
// create the images amd assign to class member variable
NSString *imgStr1 = [[NSBundle mainBundle] pathForResource:#"someImg1"
ofType:#"png"];
UIImage* img1 = [[UIImage alloc] initWithContentsOfFile:imgStr];
self.image1 = img1;
[img1 release];
NSString *imgStr2 = [[NSBundle mainBundle] pathForResource:#"someImg2"
ofType:#"png"];
UIImage* img2 = [[UIImage alloc] initWithContentsOfFile:imgStr2];
self.image2 = img2;
[img2 release];
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"cellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:
cellIdentifier];
if (cell == nil)
{
//create image views here
..........................
}
/ assign images from viewDidLoad to imageView here
UIImageView *img1View = (UIImageView *)[cell viewWithTag:kImg1Tag];
[img1View setImage:self.img1];
etc....
}
OR should i just do this in the cellForRowAtIndexPath
[img1View setImage:[UIImage imageNamed:#"img1.png"];
In this case I would go with imageNamed: as it will cache the two images and properly respond to memory warning situations.
Method one is valid, but there is little difference between it and using imageNamed:. Images created with imageNamed: will be cleared out if the device needs to reclaim memory. Unless you clear the images created in method one yourself when you receive a memory warning they will stay in memory.
It's also less code and less that you have to worry about, which is always better. Less code == less bugs.
I think the simplest way is to use UIImage's imageNamed: method, which loads the image from the app bundle and keeps it in cache.
This way you would only have to set the cell's UIImageView's image to [UIImage imageNamed:#"img1.png"] in cellForRowAtIndexPath: method.
Another point, if you cell has many subviews, I think subclassing it and adding different subviews as class properties is better. Then you only have to cast it when getting it from dequeueReusableCell and it allows you to modify subviews without using tags and casting everytime.

How to set the table view cell accessory view to retain a previously initialized UIImageView?

Let's say I have a property in my view controller, defined as follows:
#property (nonatomic, retain) UIImageView *checkmarkOffAccessoryView;
I #synthesize this in the implementation, release it in -dealloc and initialize it in -viewDidLoad as follows:
self.checkmarkOffAccessoryView = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"checkmarkOff.png"]] autorelease];
So far so good.
When I use it in my table view delegate as an accessory view for multiple cells, two things happen:
Only one cell's accessory view shows the image
The application UI freezes.
The app doesn't crash, as near as I can tell, the UI simply becomes unresponsive. This is both in the simulator and on the device.
Here is how I use the initialized property with my cell:
- (UITableViewCell *) tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// initialize or dequeue cell...
if (condition)
cell.accessoryView = self.checkmarkOffAccessoryView;
else
cell.accessoryView = nil;
}
With the aforementioned code, only one cell shows the accessory view and the UI freezes.
If I initialize the UIImageView instance directly in the delegate method I get all condition-satisfying cells showing the accessory view and I do not experience the UI freeze:
- (UITableViewCell *) tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// initialize or dequeue cell...
if (condition)
cell.accessoryView = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"checkmarkOff.png"]] autorelease];
else
cell.accessoryView = nil;
}
My goal is to initialize as few objects as possible and reuse one UIImageView. I'm curious why the first chunk of code is problematic and what I could do to fix this.
It seems like the cell's accessoryView property should just increment the retain count of self.checkmarkOffAccessoryView but it appears I am missing some detail.
What have I overlooked? Thanks for your advice.
EDIT
I think that:
self.checkmarkOffAccessoryView = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"checkmarkOff.png"]] autorelease];
is the same as:
UIImageView *uncheckedView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"checkmarkOff.png"]];
self.checkmarkOffAccessoryView = uncheckedView;
[uncheckedView release];
Either way, I experience the same freeze symptom.
You cannot add the same view multiple times. The UI handler will go bonkers. To make sure of this, I tried doing what you said above and I got the same issue. The UI freezes up, the image only appears for one of the cells.
The best thing you can do is to store your image as a UIImage allocated, and to have a helper function which returns a new UIImageView per cell.
Using your current method (without a stored UIImage) you might do:
-(UIImageView *) makeCheckmarkOffAccessoryView
{
return [[[UIImageView alloc] initWithImage:
[UIImage imageNamed:#"checkmarkOff.png"]] autorelease];
}
And then do
cell.accessoryView = [self makeCheckmarkOffAccessoryView];
As you may be aware, UIImages on the other hand may be used any number of times. a UIImageView doesn't take up a lot of space, so you can easily have a bunch of those without worrying.
To expand on the one place only deal, imagine that you add a UIView to two places at the same time.
What will [ob removeFromSuperview] do for this object? Will it remove the view from both places? From one of them only? Which value will be returned when you request [ob superview]? Clearly the UI is not made to handle what you're asking for.
Try it without the autorelease in the initializer. I suspect you're over-releasing.
By the way, your console probably is showing a BAD_ACCESS error when it freezes. If you turn on NSZombieEnabled, my guess is you'll see it's making a call to a deallocated UIImage.
maybe this will help
- (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"ShoppingListCell";
HSShoppingListCell *cell = (HSShoppingListCell *)[aTableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
[[NSBundle mainBundle] loadNibNamed:#"ShoppingListCell"
owner:self
options:nil];
cell = shoppingListCell;
}
ShoppingListItem *theItem = nil;
theItem = [self.fetchedResultsController objectAtIndexPath:indexPath];
UIImage *selected = [UIImage imageNamed:#"listBullet_checked.png"];
UIImage *notSelected = [UIImage imageNamed:#"listBullet.png"];
cell.imageView.image = ([theItem.checkedOff boolValue] ? selected : notSelected);
cell.shoppingListLabel.text = theItem.productName;
[cell.shoppingListLabel setFont:[UIFont fontWithName:#"Marker Felt" size:26.0]];
return cell;
}
- (void)toggleCellImage:(NSIndexPath *)indexPath
{
ShoppingListItem *item = [self.fetchedResultsController objectAtIndexPath:indexPath];
item.checkedOff = ([item.checkedOff boolValue] ? [NSNumber numberWithBool:NO] : [NSNumber numberWithBool:YES]);
[HSCoreDataUtilities saveContext:item.managedObjectContext];
[self.tableView reloadData];
}
#pragma mark -
#pragma mark Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self toggleCellImage:indexPath];
[self.tableView deselectRowAtIndexPath:indexPath animated:YES];
}
Reducing your case to the bare essentials (I was going to suggest to put two 'thin' UIView objects around the UIImageView...), I found that it is most probably impossible.
Create 2 empty UIView objects in IB, hook them up to bareView1 and bareView2. Then
UIImageView *imageView = [[UIImageView alloc]
initWithImage:[UIImage imageNamed:#"test.png"]];
[bareView1 addSubview:imageView]; // it shows either here ...
[bareView2 addSubview:imageView]; // ... or here
You can never get the image on sceen more than once like this. As a rule of thumb, I think the first object in line which does not inherit from UIView can be used multiple times, i.e. the UIImage. Like Kalle stated, a UIView can only have one parent in the view hierarchy.
Postponing the second addSubview only makes the UIImageView jump from bareView1 to bareView2.
The freeze happens maybe because the event handling gets mixed up: the accessory can be interactive, how would you know which one was tapped if they are one and the same object? So the code assumes objects are unique, and you manage to violate that assumption.