I'm loading images into my UITableViewCell and it is becoming choppy. I only have around 5-10 cells max.
Are there any easy ways to fix this?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"appointmentCell";
AppointmentTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
NSDictionary *appointmentDictionaryTemp = [self.appointmentArray objectAtIndex:indexPath.row];
cell.patientNameLabel.text = [appointmentDictionaryTemp objectForKey:#"patient"];
cell.appointmentTimeLabel.text = [appointmentDictionaryTemp objectForKey:#"scheduled_time"];
NSString *urlString = [[appointmentDictionaryTemp objectForKey:#"patient_small_photo_url"] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSData * imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:urlString]];
UIImage * image = [UIImage imageWithData:imageData];
cell.patientImage.image = image;
return cell;
}
dataWithContentsOfURL: is your culprit. It performs a synchronous (blocking) network request on the application's main UI thread, causing the table view to lock up while an image is being downloaded.
The solution is much more involved, and it involves using NSURLConnection (or some other 3rd party library) to download the data asynchronously, allowing the UI to remain responsive while the image is being downloaded. This is a good resource to use for reference.
Create an ivar called imageCache, which is an NSArray. Now run this code in init:
for (NSDictionary *appointmentDictionaryTemp in self.appointmentArray) {
NSString *urlString = [[appointmentDictionaryTemp objectForKey:#"patient_small_photo_url"] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSData * imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:urlString]];
UIImage * image = [UIImage imageWithData:imageData];
[imageCache addObject:image];
}
Now in cellForRowAtIndexPath:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"appointmentCell";
AppointmentTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
NSDictionary *appointmentDictionaryTemp = [self.appointmentArray objectAtIndex:indexPath.row];
cell.patientNameLabel.text = [appointmentDictionaryTemp objectForKey:#"patient"];
cell.appointmentTimeLabel.text = [appointmentDictionaryTemp objectForKey:#"scheduled_time"];
cell.patientImage.image = [imageCache objectAtIndex:indexPath.row];
return cell;
}
Load Images asynchronusly and maybe cache them. Start with reading this question: Lazy load images in UITableViewCell
store the images in an NSCache using the index path as the key, then in your
tableView: cellForRowAtIndexPath: method, check to see if there is an object for the index path. if there is load it into the image property, and if not, then call a method to generate it. this will stop it being done every time a cell is drawn.
There is an open source (MIT license) implementation of lazy/cached network of UIImageView images which you should take a look at: SDWebImage. Downloads/caches images asynchronously and automatically updates your UIImageViews.
Related
Sequence of audio files in collection view cell is wrong. In first cell it plays nothing, in second cell it plays first audio file, in third cell it plays second audio file , in fourth cell it plays third audio file and when clicking on first cell again it plays fourth audio file.
Why is that. What i m doing wrong in code.
Here is my code
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
audioArray =[[NSArray alloc] initWithObjects:#"0", #"1", #"2", #"3", nil];
NSString *filePath = [audioArray objectAtIndex:indexPath.row];
NSString *audioFilePath = [[NSBundle mainBundle] pathForResource:audioToLoad ofType: #"mp3"];
NSURL *fileURL = [[NSURL alloc] initFileURLWithPath: audioFilePath];
audioPlayer = [[AVAudioPlayer alloc]
initWithContentsOfURL:fileURL error:nil];
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)cv cellForItemAtIndexPath:(NSIndexPath *)indexPath;
{
// custom UICollectionViewCell, hold an image and its label
Cell *cell = [cv dequeueReusableCellWithReuseIdentifier:kCellID forIndexPath:indexPath];
// make the cell's title the actual NSIndexPath value
cell.label.text = [NSString stringWithFormat:#"{%ld,%ld}", (long)indexPath.row, (long)indexPath.section];
// load the image for this cell
NSString *imageToLoad = [NSString stringWithFormat:#"%d.jpg", indexPath.row];
cell.image.image = [UIImage imageNamed:imageToLoad];
cell.backgroundColor = [UIColor whiteColor];
return cell;
}
why audio files from array is not loading in correct order in the collection view cells.
Thanks for help.
I know this is old, but will try to answer it anyway.
Usually when you're working with the collection view you specify the type of file that corresponds to the project files that you wish to show. So for example, if you were loading images you would specify the type as .jpg or .png etc. For audio it might be .aiff or .mp3 .wav etc.
Once you've done this you'd use the %ld which represents numbers starting from 0. that would assist in loading your files in the correct order.
https://developer.apple.com/library/ios/samplecode/CollectionView-Simple/Introduction/Intro.html
Would give you a good starting point on collection view method alternatives - this uses the named method and applies to .jpg try chaning the code to suite for audio.
Im trying to load the image of my uitableviewcells in lazy mode.
I'm trying to do it in the simplest possible way, I saw a lot of examples but they were going further than my purpose.
This is what Im doing currently, and its not working:
// Configure the cell...
Info *info = [self.Array objectAtIndex:indexPath.row];
cell.textLabel.text = info.name;
cell.detailTextLabel.text = info.platform;
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
//The image is downloaded in asynchronous
NSBlockOperation *downloadingImgBlock = [NSBlockOperation blockOperationWithBlock:^{
NSString* imageURL = info.imgURL;
NSData* imageData = [[NSData alloc]initWithContentsOfURL:[NSURL URLWithString:imageURL]];
cell.imageView.image = [UIImage imageWithData:imageData];
}];
[self.queue addOperation:downloadingImgBlock];
Why it's not working? And how could it work?
Man, AsyncImageView is your friend! Just set the image url and everything is handled for you, downloading, caching. It's awesome.
Try the following codes
......
__block UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
__block UIImage *img;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
img = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString: imageURL]]];
dispatch_async(dispatch_get_main_queue(),^{
cell.imageView.image = img;
}
});
});
......
Using the 3rd party library source code would be easiest, but here's how you would do it in code. You are going to want to make a NSMutableArray either as a property in your .h or at the top of your .m like this:
#implementation viewController{
NSMutableArray *picList;
}
and in -initWithCoder: or, whatever init you are overloading:
picList = [[NSMutableArray alloc] init];
in – tableView:cellForRowAtIndexPath: (fullPicURL is the URL):
if([self imageExists:fullPicURL]){
shoePic = [picList objectForKey:fullSmallPicURL];
} else {
NSURL *picURL = [NSURL URLWithString:fullPicURL];
shoePic = [UIImage imageWithData:[NSData dataWithContentsOfURL:picURL]];
NSDictionary *thisImage = [NSDictionary dictionaryWithObject:shoePic forKey:fullSmallPicURL];
[cachedImages addEntriesFromDictionary:thisImage];
}
where -imageExists: is a function you write which checks the dictionary for the url. The object is the picture itself, the key is the url. Then you do cell.image = showPic;, or whatever you want to call it, and you're done for cacheing. In order to load them asynchronously, do this:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData *data = [NSData dataWithContentsOfURL:shoes];
[self performSelectorOnMainThread:#selector(fetchedShoeData:) withObject:data waitUntilDone:YES];
});
and at the end of fetchedData:
[self.tableView reloadData];
then just make sure you edit –numberOfSectionsInTableView: to change itself if it has to. Might be some other things you need to do to get it working 100%, let me know
try this one, I'm using always this scheme to load images asynchronously:
(I haven't found simplest way.)
- (void)inAnyMethod {
// ...
void (^blockLoadPhoto)() = ^{
UIImage *_imageTemporary = [UIImage imageNamed:#"..."];
if (_imageTemporary) {
[self performSelectorOnMainThread:#selector(setPhoto:) withObject:_imageTemporary waitUntilDone:true];
}
};
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), blockLoadPhoto);
// ...
}
- (void)setPhoto:(UIImage *)photo {
// do something with the loaded image
}
I finally managed to do it setting the image with:
– performSelectorOnMainThread:withObject:waitUntilDone:
After the image is downloaded
Here is the code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"ConvoreCell";
ConvoreCell * cell = (ConvoreCell *) [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:#"ConvoreCell" owner:nil options:nil];
for (id currentObject in topLevelObjects){
if ([currentObject isKindOfClass:[UITableViewCell class]]){
cell = (ConvoreCell *) currentObject;
break;
}
}
}
NSMutableDictionary *cellValue = [results objectAtIndex:indexPath.row];
NSString *picURL = [[cellValue objectForKey:#"creator"] objectForKey:#"img"];
if ([picURL isEqualToString:#"https://secure.gravatar.com/avatar/1f043010eb1652b3fab3678167dc0487/?default=https%3A%2F%2Fconvore.com%2Fmedia%2Fimages%2Feric.png&s=80"])
picURL = #"https://convore.com/media/images/eric.png";
if ((picURL != (NSString *) [NSNull null]) && (picURL.length !=0)) {
NSLog(#"%#", picURL);
NSData *imgData = [[[NSData dataWithContentsOfURL:
[NSURL URLWithString:
picURL]] autorelease] retain];
[cell.pic initWithImage:[[UIImage alloc] initWithData:imgData]];
} else {
cell.pic = nil;
}
//NSLog(#"Name is : %#", [cellValue objectForKey:#"name"]);
cell.title.text = [cellValue objectForKey:#"name"];
cell.info.text = (#"Created by: %#", [[cellValue objectForKey:#"creator"] objectForKey:#"name"]);
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
// Configure the cell...
return cell;
}
For some reason the image is not showing up. What am I doing wrong here? The URL of the image is valid, I checked.
Also I don't want to have the accessory view on the TableViewCell, I did specify that as no in IB, but it still shows up... i set the accessory attribute to none already
you have some issues in your code.
NSData *imgData = [[[NSData dataWithContentsOfURL:
[NSURL URLWithString:
picURL]] autorelease] retain];
the former is not a real issue (maybe by coincidence?!), but it's ugly and confusing. get rid of the autorelease and the retain. The object you get from dataWithContentsOfURL: is already autoreleased.
[cell.pic initWithImage:[[UIImage alloc] initWithData:imgData]];
And your real issue might be this. From what I know you should call init exactly one time; after you've allocated an object.
So if cell.pic is an UIImageView I would try to change the code to something like this:
cell.pic.image = [[[UIImage alloc] initWithData:imgData] autorelease];
Once you see your images you should change your code so that it saves the images in the data you use to populate the cell. You are downloading the image every time a cell appears. That's something you shouldn't do. But this is beyond this question.
EDIT:
Also I don't want to have the
accessory view on the TableViewCell, I
did specify that as no in IB, but it
still shows up... i set the accessory
attribute to none already
You do? But I can see this line of code:
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
You should be able to get an UIImage by just
[cell.pic initWithImage:[UIImage imageWithData:[NSData dataWithContentsOfURL:[NSUrl NSURL picURL]]]];
You're not showing your cellForRowAtIndexPath method, which is the key to analyzing what's going on. The typical paradigm is that UITableViewCell objects get reused repeatedly by the UITableView. You should be setting the UIImage into the cell when the UITableView calls cellForRowAtIndexPath.
The problem causing your immediate crash is that the nib for ConvoreCell is trying to set an IBOutlet you have declared but finding no way to actually set it. I would check that you have synthesized your pic property.
There are other problems here. The line
[cell.pic initWithImage:[[UIImage alloc] initWithData:imgData]];
Is wrong; you do not call init on an object that already exists, but only when you are allocating an object. Do something like
cell.pic = [[[WhateverClassPicIS alloc] initWithImage:[[UIImage alloc] initWithData:imgData]];
Also, you are using NSData dataWithContentsOfURL on the main thread. This will lock up your user interface for an indeterminate time as you connect to the network. Use asynchronous calls instead.
I built a simple mac data entry tool I use with an iPhone application. I've recently added thumbnail which I added via an Image Well using simple bindings. Its a transformable data type which seems to work fine.
The iPhone application however won't show the images. The attribute isn't null but I can't get an image to appear. The following is for cellForRowAtIndexPath
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
NSManagedObject *entity = nil;
if ([self.searchDisplayController isActive])
entity = [[self filteredListContent] objectAtIndex:[indexPath row]];
else
entity = [fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = [entity valueForKey:#"name"];
//cell.imageview.image = [UIImage imageNamed:#"ImageB.jpeg"]; //works fine
cell.imageView.image = [entity valueForKey:#"thumbnail"];//no error, but no file
return cell;
I'm thinking either the problem is with the transformable (I'm using the default NSKeyedUnarchiveFromData), or how I'm calling the thumbnail. I'm a newbie so any help would be greatly appreciated.
Sounds like you are storing the image as an NSImage on the desktop and that object does not exist on the iPhone. Your desktop app needs to store the image in something that is portable, a PNG or JPG, etc. Then you will be able to load it back into your iPhone application as a UIImage.
Update re transformable
Sounds like you are still passing in a NSImage to the attribute and it is thinking you are handling it data. You need to convert it to a "standard" format first, like this:
NSBitmapImageRep *bits = [[myImage representations] objectAtIndex: 0];
NSData *data = [bits representationUsingType:NSPNGFileType properties:nil];
[myManagedObject setImage:data];
I recommend writing custom accessors to handle this, like the following:
#ifdef IPHONEOS_DEPLOYMENT_TARGET
- (void)setImage:(UIImage*)image
{
[self willChangeValueForKey:#"image"];
NSData *data = UIImagePNGRepresentation(image);
[myManagedObject setImage:data];
[self setPrimitiveValue:data forKey:#"image"];
[self didChangeValueForKey:#"image"];
}
- (UIImage*)image
{
[self willAccessValueForKey:#"image"];
UIImage *image = [UIImage imageWithData:[self primitiveValueForKey:#"image"];
[self didAccessValueForKey:#"image"];
return image;
}
#else
- (void)setImage:(NSImage*)image
{
[self willChangeValueForKey:#"image"];
NSBitmapImageRep *bits = [[image representations] objectAtIndex: 0];
NSData *data = [bits representationUsingType:NSPNGFileType properties:nil];
[myManagedObject setImage:data];
[self setPrimitiveValue:data forKey:#"image"];
[self didChangeValueForKey:#"image"];
}
- (NSImage*)image
{
[self willAccessValueForKey:#"image"];
NSImage *image = [[NSImage alloc] initWithData:[self primitiveValueForKey:#"image"]];
[self didAccessValueForKey:#"image"];
return [image autorelease];
}
#endif
This will give you a conditional compile and will store the data as NSData (in PNG format) that can be retrieved on any device.
I am new to iphone development.I want to display image in each cell.I am having only the url of the image in a array.please help me out.Thanks.
//put this code in cellForRowAtIndexPath.... I'm assuming you have an array of url's that point to images
id urlPath = [yourArray objectAtIndex:indexOfImage];
NSURL *url = [NSURL URLWithString:urlPath];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
//insert an image into your cell
cell.imageView = image;
There is probably a more efficient way to do this, but it's a decent way to get started customizing your tableview cells. ImageView is one of many properties of the UITableViewCell class.
Further reading.... UITableViewCell Class Reference
You should take a look at this apple's sample code, it is great and shows how to implement what you need plus other useful stuff:
Advanced Table View Cells