My app needs to display huge number of images(about 2000) in a UITableView. Basically, I use the following code to construct my UITableViewCell:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell"];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"Cell"];
}
// Some Operations...
NSString *path = [self.dataArray jk_objectWithIndex:indexPath.row];
UIImage *img = [UIImage imageWithContentsOfFile:path];
cell.imageView.image = img;
return cell;
}
This works but when the tableview loads, memory increase fast and it seems that all the images is loaded to the memory.
Is there any good ideas to deal with this? I just want to save the memory.
BTW, anyone knows what the common way is to achieve this need? I think loading all the images to memory is the stupidest way... And the code I initial rows of tableview is the following:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (!_isLoading) {
return self.dataArray.count; // about 2000... That's terrible
} else {
return 0;
}
}
Thanks!
There are two problems with your code.
First, and most important, the images are large but the display of the images in the table is small. That's wrong. You should load the image only at the size you actually need for display.
Second, images are cached by default. You need to prevent the caching of these images as they are loaded.
You can easily do both of those things in your cellForRowAt by using the ImageIO framework.
I figure out a way to this question. Add these lines of code to cellForRowAtIndexPath:
CGRect rectInTableView = [tableView rectForRowAtIndexPath:indexPath];
CGRect rectInSuperview = [tableView convertRect:rectInTableView toView:[tableView superview]];
if ( rectInSuperview.origin.y > SCREEN_HEIGHT || rectInSuperview.origin.y + rectInSuperview.size.height < 0 ) {
cell.imageView.image = self.placeholder;
} else {
NSString *path = [self.dataArray jk_objectWithIndex:indexPath.row];
UIImage *img = [UIImage imageWithContentsOfFile:path];
cell.imageView.image = img;
}
First I check whether the cell is shown on the screen. If yes, the imageView show my data images. If not, it show the placeholder instead. Also, the placeholder is init with [UIImage imageNamed:]. This is the best way because it will be used frequently.
Related
I added images to my tableView cells and it made it laggy. I am still new to objective c and I do not understand what is causing this or how to fix it. Any help is greatly appreciated!
group[PF_GROUP_LOGO] is simply a string in my database that is unique to each object. The code works, it is just really laggy when trying to scroll.
//-------------------------------------------------------------------------------------------------------------------------------------------------
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
//-------------------------------------------------------------------------------------------------------------------------------------------------
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell"];
if (cell == nil) cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"cell"];
PFObject *group = groups[indexPath.row];
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:group[PF_GROUP_LOGO]]]];
cell.imageView.image = image;
cell.detailTextLabel.text = [NSString stringWithFormat:#"%d users", (int) [group[PF_GROUP_MEMBERS] count]];
cell.detailTextLabel.textColor = [UIColor lightGrayColor];
return cell;
}
There are so many tools out there that can help you with this.
At a base level, the issue is that you are running a long process on the main thread, which blocks the UI. Loading an image from a non-local URL is time consuming, and you should do this on a background thread, so the UI is not blocked.
Again, there are so many ways to do this, and I strongly suggest you do some research on async resource loading, but this is one thing you can do within the confines of your own example:
//-------------------------------------------------------------------------------------------------------------------------------------------------
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
//-------------------------------------------------------------------------------------------------------------------------------------------------
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell"];
if (cell == nil) cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"cell"];
PFObject *group = groups[indexPath.row];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ // go to a background thread to load the image and not interfere with the UI
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:group[PF_GROUP_LOGO]]]];
dispatch_async(dispatch_get_main_queue(), ^{ // synchronize back to the main thread to update the UI with your loaded image
cell.imageView.image = image;
});
});
cell.detailTextLabel.text = [NSString stringWithFormat:#"%d users", (int) [group[PF_GROUP_MEMBERS] count]];
cell.detailTextLabel.textColor = [UIColor lightGrayColor];
return cell;
}
I would also recommend using AFNetworking as the author has built a very good category on top of UIImageView that allows you to load an image from a web URL in the background automatically. Again, there are many schools of thought on this process, and this is just one idea. I would recommend reading this for a full on tutorial on the topic.
I hope this is helpful!
I am working on a project in which I have to put images on table view cells.
Is it possible to put .pdf format files on table view cells as image?
I used the following code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (!cell)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
// NSDictionary *menuAttr = [menuItems objectAtIndex:indexPath.row];
// cell.textLabel.text = [NSString stringWithFormat:#"%#", [menuAttr objectForKey:#"name"]];
if (indexPath.row ==0 )
{
cell.imageView.image = [UIImage imageNamed:#"about us.pdf"];
cell.imageView.layer.cornerRadius =cell.imageView.image.size.width/2;
}
if (indexPath.row == 3)
{
cell.imageView.image = [UIImage imageNamed:#"policies.pdf"];
}
if (indexPath.row == 2)
{
cell.imageView.image = [UIImage imageNamed:#"services.pdf"];
}
if (indexPath.row == 1)
{
cell.imageView.image = [UIImage imageNamed:#"home.pdf"];
}
if (indexPath.row == 4)
{
cell.imageView.image = [UIImage imageNamed:#"contact us.pdf"];
}
if (indexPath.row == 5)
{
cell.imageView.image = [UIImage imageNamed:#"feedback.pdf"];
}
[tableView setSeparatorStyle:UITableViewCellSeparatorStyleNone];
cell.backgroundColor = [UIColor clearColor];
[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
cell.contentView.backgroundColor = [UIColor clearColor];
return cell;
}
Yes, you sure can, but it's only going to give you a shot of the first page of the PDF ... almost like a thumbnail. Here's how you do it:
Create an assets folder in Xcode:
Place pdf files into assets catalog:
Change the image so it's the correct format, look on the right side of this picture, you have to change the 5th drop down on the side to "single vector":
Drag image into correct slot, in the previous image you see the yellow warning marker? This happens because the image isn't in the correct slot, you have to drag the PDF file to the slot above to correct this:
you can use this PDF vector file like this:
[_window setBackgroundColor:[UIColor colorWithPatternImage:[UIImage imageNamed:#"examplepdf"]]];
I only use vectorized pdfs in my apps. The system resizes these for the correct iphone and ipad sizes by itself, no need to use #2x or #3x or anything else.
Here's an additional tutorial on how this works:
http://martiancraft.com/blog/2014/09/vector-images-xcode6/
You really should FIRST save the PDF files in Illustrator as explained in the tutorial above, but either way, this method should work. Good luck!
Here's the output from the small tutorial I just explained above:
You can draw PDFs using CoreGraphics:
Open the PDF using CGPDFDocumentCreateWithURL().
Get the (first or any other) page using CGPDFDocumentGetPage().
Draw the page into your own context with CGContextDrawPDFPage().
You need to have the drawing context, so either:
Create UIView subclass, override -drawRect: and use it instead of UIImageView.
Setup an image context using UIGraphicsBeginImageContextWithOptions () so you will get an UIImage instance.
I am a newbie in iOS.....I have created a table view to display as a contact view..... here i have displayed name and number using the below coding but i was not able to display images even though i used a correct code..... plz help me
names = [NSMutableArray arrayWithObjects:#"Karthick", #"Gopal", #"Suresh", #"Senthil", #"Guna",nil];
images = [NSMutableArray arrayWithObjects:#"per.png",#"per.png",#"per.png",#"per.png",#"per.png", nil];
num = [NSMutableArray arrayWithObjects:#"9568421301", #"8756324103", #"856472303", #"8796523565", #"9785858569",nil];
This is my three arrays and
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [names count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"SimpleTableItem";
contactTableViewCell *cell = (contactTableViewCell *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
cell = [[contactTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}
cell.name.text = [names objectAtIndex:indexPath.row];
cell.number.text = [num objectAtIndex:indexPath.row];
cell.conimage.image = [UIImage imageNamed:[names objectAtIndex:indexPath.row];
return cell;
}
You have to set images instead of names in cellForRowAtIndexPath method as follows
cell.conimage.image = [UIImage imageNamed:[images objectAtIndex:indexPath.row];
replace the line in cellForRowAtIndexPathwith the above line
I see to possible issues:
UIImage +imageNamed: only works for images added to the main bundle. check, if you did that.
From the docs:
Declaration
+ (UIImage *)imageNamed:(NSString *)name
Parameters
name The name of the file. If this is the first time the image is being loaded, the method looks for an image with the specified name
in the application’s main bundle.
The other issue could be that the UIImageView conimage is never created.
If this is the case, cell.conimage.image = [UIImage imageNamed:[names objectAtIndex:indexPath.row]]; would be identical to [nil setImage:aImage]; and in contrast to other languages as Java this is a valid call. Any message sent to the nil object will result in nil — and not an exception.
To be able to see, if this indeed is the case, you will have to explain (preferable with code) how your custom cell class looks like.
I want to update button images in my table view. When my table view initially loads, the button images are correct. But when I scroll down the new cells don't update to my new images; they simply reuse a random old image from the initial set. I have tried setImage and setBackgroundImage. Here is my cellForRowAtIndexPath:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.section == 0)
{
SSKGrooveCell *cell = [self.tableView dequeueReusableCellWithIdentifier:#"grooveCell" forIndexPath:indexPath];
SSKSongCellInfo *entry = [_objects objectAtIndex:indexPath.row];
NSArray *icons = [plistHelper iconsForGroove:entry.metadata.title];
NSString *icon = [icons objectAtIndex:0];
NSString *iconImgString = [NSString stringWithFormat:#"element_%#_grooves", icon];
NSString *iconImgWithExt = [NSString stringWithFormat:#"%#.png", iconImgString];
[cell.inst1Button setBackgroundImage:[UIImage imageNamed:iconImgWithExt] forState:UIControlStateNormal];
cell.titleLabel.text = entry.metadata.title;
return cell;
}
return nil;
}
For a quick test, add this line in your cellForRowAtIndexPath
cell.imageView.image = [UIImage imageNamed:iconImgWithExt];
Do you see correct images?
If no, check your images and image names
If yes, check your inst1Button IBOutlet connection.
I just came across this issue on an app. Turns out there's a bug in iOS 7.1 regarding a UIButton in a UITableView. You need to call setNeedsLayout on the button after you've set the image. E.g.:
[cell.inst1Button setNeedsLayout];
Hi found the answer here: Images getting mixed up in a UITableView - XML parsing
I'm parsing an XML file with links to images which I'm putting into a UITable. For some reason the pictures are getting completely mixed up when I scroll down in the table. Here's the code I'm using for my UITable:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
Tweet *currentTweet = [[xmlParser tweets] objectAtIndex:indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
CGRect imageFrame = CGRectMake(2, 8, 40, 40);
customImage = [[UIImageView alloc] initWithFrame:imageFrame];
[cell.contentView addSubview:customImage];
}
NSString *picURL = [currentTweet pic];
if (![picURL hasPrefix:#"http:"]) {
picURL = [#"http:" stringByAppendingString:picURL];
}
customImage.image = [UIImage imageWithData:[NSData dataWithContentsOfURL: [NSURL URLWithString:picURL]]];
return cell;
}
Any idea what I'm doing wrong? Any help is seriously appreciated. Thx!
Its because you are dequeueing reusable cells. It is recycling a cell that had a previously used image in it.
I noticed that you are setting the image in the same if block that you are initializing the cell. If I were you I'd move [cell.contentView addSubview:customImage]; to right before your return statement. This way when it recycles a dequeued cell, it won't have an image in it.
Are the images being downloaded from the web?
If so then the delay in downloading the images will be causing this. You'll need to do it in a multi threaded way.
Take a look at this example...
Question about Apple's LazyTableImages sample - Doesn't behave exactly like the app store
There are also many tutorials online about lazy loading with images and UITableViews.