iPhone UITableViewCell slow performance - performance

I just wrote a small application that read from a site feed and display in UITableViewCell. I am using custom view cell and my UITableView is screwed in scrolling like it is not very smooth in scrolling upside down. Any idea? Here's the code for my UITableViewCell,
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"CustomCell";
CustomCell *cell = (CustomCell *) [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
// cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:#"CustomCell" owner:nil options:nil];
for(id currentObject in topLevelObjects) {
if([currentObject isKindOfClass:[UITableViewCell class]]) {
cell = (CustomCell *) currentObject;
break;
}
}
}
//MiceAppDelegate *AppDelegate = (MiceAppDelegate *)[[UIApplication sharedApplication] delegate];
if(dataArray != nil) {
//
//NSArray *promoArray = (NSArray *) promotions;
NSDictionary *datadict = [self.dataArray objectAtIndex:indexPath.row];
NSString *url = [datadict objectForKey:#"imageUrl"];
NSString *title = [datadict objectForKey:#"title"];
NSString *description = [datadict objectForKey:#"description"];
NSString *newAddressPartOfURL = [url stringByReplacingOccurrencesOfString:#" " withString:#"+"];
//NSLog([url stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding]);
NSURLResponse *urlResponse;
NSData *data = [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:newAddressPartOfURL]] returningResponse:&urlResponse error:nil];
// Configure the cell.
UIImage *urlImage = [[UIImage alloc] initWithData:data];
// NSLog(#"%i",[imageView.image topCapHeight]);
cell.title.text = title;
cell.description.text = description;
cell.image.image = urlImage;
[urlImage release];
}
return cell;
}

Doing synchronous downloads as your cells are being drawn is definitely going to cause some unsmooth scrolling. You could try to replace those with asynchronous calls, and filling in the data with a generic object while the download is happening. When the download completes, then call reloadData on your tableview.

afaik the dequeueReusableCellWithIdentifier method is called as cells get flush etc. Build your data / do the requests on init, not in the cell creation!

Related

Loading Images into a tableview cell, according to object loaded from NSUserDefaults

I have a tableview populated by objects loaded from NSUserDefaults.
Specifically - when a user presses a button in one view controller, a string is loaded into NSUserDefaults, which is then loaded into an array, which finally populates a table view.
I am wondering how I can load an image in each tableview cell, dependent on what string is loaded from NSUserDefaults?
I don't want to save images IN NSUserDefaults, as I am aware this is not a good idea.
What can I do?
Thank you for your time (and helping a novice out)!
Update 19FEB13
The Strings I am loading into NSUserDefaults are the names of objects, for example "ARCX".
At the moment I have tried a variety of pieces of code, but I am at the point where I seem to have a misunderstanding/lack of knowledge (or both).
UITableViewCell *XCell = [tableView cellForRowAtIndexPath:IndexPath];
NSString *content = Xcell.textLabel.text;
if ([content isEqualToString:#"ARCX"]) [UIImage *Image = [UIImage imageNamed #"ARCX.png"]];
Obviously I'm getting errors from this code.
I hope you can help!
Update 20FEB13
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *string = [myOBJArray objectAtIndex:indexPath.row];
static NSString *CellIdentifier = #"XCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
UIView *cellbackground = [[UIView alloc] initWithFrame:CGRectZero];
cellbackground.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"MSP cell bg MK2.png"]];
cell. backgroundView = cellbackground;
}
cell.textLabel.textColor = [UIColor yellowColor ];
cell.textLabel.font = [UIFont fontWithName:#"Helvetica" size:24.0];
cell.textLabel.text = string;
UITableViewCell *XCell = [tableView cellForRowAtIndexPath:indexPath];
NSString *content = XCell.textLabel.text;
if ([content isEqualToString:#"ARCX"]) [UIImage *Image = [UIImage imageNamed: #"ARCX.png" ]];
cell.imageView.image = Image;
return cell;
}
I did have the code I initially posted in the CellForRowAtIndexPath, but I know I am missing something in the implementation (obviously as it doesn't work!).
Thanks for your time!
Update 19Feb13
I This is my current CellForRowAtIndexPath. No errors, but yet no images in my cells. What am I missing?
Have I got to add an image view? I am currently using a 'basic' cell, not a custom one.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *string = [myObjArray objectAtIndex:indexPath.row];
static NSString *CellIdentifier = #"MSCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
UIView *cellbackground = [[UIView alloc] initWithFrame:CGRectZero];
cellbackground.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"MSP cell bg MK2.png"]];
cell. backgroundView = cellbackground;
}
cell.textLabel.textColor = [UIColor blackColor ];
cell.textLabel.font = [UIFont fontWithName:#"Helvetica" size:24.0];
cell.textLabel.text = string;
if ([string isEqualToString:#"ARCX"])
cell.imageView.image = [UIImage imageNamed: #"ARCX.png"];
else if ([string isEqualToString:#"WPX"])
cell.imageView.image = [UIImage imageNamed:#"WPX.png"];
return cell;
}
Thanks for your time!
UITableViewCell *XCell = [tableView cellForRowAtIndexPath:indexPath];
NSString *content = XCell.textLabel.text;
Please remove this piece of code, as it will call cellForRowAtIndexPath again and again.
You already have the image name as string
NSString *string = [myOBJArray objectAtIndex:indexPath.row];
So change the code like below.
if ([string isEqualToString:#"ARCX"])
UIImage *Image = [UIImage imageNamed: #"ARCX.png"];
cell.imageView.image = Image;

Custom cells - how can I separate out tweets into componentsseparatedbyString

I have a custom cell in a tableview. All connections are made. I am conducting a Twitter search. The results of this propagate a custom cell in a tableView. I would like to separate the individual tweets out into an array using componentsseparatedbystring so that I can assign these to 4 labels in my custom cell. Any help would be greatly appreciated. Here is my code.
- (void)fetchTweets
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData* data = [NSData dataWithContentsOfURL:
[NSURL URLWithString: #"https://api.twitter.com/1/statuses/public_timeline.json"]];
NSError* error;
tweets = [NSJSONSerialization JSONObjectWithData:data
options:kNilOptions
error:&error];
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
});
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return tweets.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"TweetCell";
customCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
// added this bit in
cell = [[customCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// THIS IS THE BIT I'M STRUGGLING WITH
// I'M GUESSING THAT THIS LINE SEPARATES THE TWEETS INTO SINGLE TWEETS?
NSDictionary *tweet = [tweets objectAtIndex:indexPath.row];
// I THEN CREATE AN ARRAY TO HOLD MY COMPONENTS OF THE TWEET SO THAT I CAN SPLIT FOR MY LABELS
NSArray *arrayForCustomCell = [[NSArray alloc] init];
arrayForCustomCell = [tweet componentsSeparatedByString:#":"];
return cell;
}
You have already parsed the results in your async call. Now, you have an array of 'tweet' dictionaries and you can grab any value like this:
NSDictionary *tweet = [tweets objectAtIndex:indexPath.row];
NSString *tweetText = [tweet valueForKey:#"text"];
NSString *tweetCountry = [tweet valueForKeyPath:#"place.country"] //Nested property
and then you just set your UILabels. You get the idea...

Resizing Cells in my project

I have this table with custom cells, how cal cells be resizable according to the content?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"CustomTableCell";
static NSString *CellNib = #"DetailViewCell";
DetailViewCell *cell = (DetailViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:CellNib owner:self options:nil];
cell = (DetailViewCell *)[nib objectAtIndex:0];
}
cell.accessoryType = UITableViewCellAccessoryNone;
cell.cellTitleLabel.textColor = [UIColor blackColor];
cell.cellSubtitleLabel.textColor = [UIColor darkGrayColor];
informations = [[NSArray alloc] initWithObjects:titleString, subtitleString, stateString, categoryString, populationString, nil];
subtitles = [[NSArray alloc] initWithObjects:#"City", #"Country", #"State", #"Category", #"Population", nil];
cell.cellTitleLabel.text = [informations objectAtIndex:indexPath.row];
cell.cellSubtitleLabel.text = [subtitles objectAtIndex:indexPath.row];
return (DetailViewCell *) cell;
}
And here is the DetailViewCell.m
#import "DetailViewCell.h"
#implementation DetailViewCell
#synthesize cellTitleLabel;
#synthesize cellSubtitleLabel;
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code.
}
return self;
}
- (void)dealloc {
[cellTitleLabel release];
[cellSubtitleLabel release];
[super dealloc];
}
Thanks!
After your cell content changed, reset cell height in - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath, and call [cell setNeedsDisplay]; to draw cell again.

How to solve slow scrolling in UITableView - using the YouTube API

I'm working with the google YouTube API, and I'm using this code found here http://pastebin.com/vmV2c0HT
The Code that slows things down is this one here
cell.imageView.image = [UIImage imageWithData:data];
When I remove that code it scrolls smoothly. Any idea on how I can go about having it load the images from google but still scroll smoothly? I've read some of the answers on loading images in a different way, but I still can't figure out how I'll make that work with the code I'm using.
Any help will be appreciated.
Bellow is the full code.
#import "RootViewController.h"
#interface RootViewController (PrivateMethods)
- (GDataServiceGoogleYouTube *)youTubeService;
#end
#implementation RootViewController
#synthesize feed;
- (void)viewDidLoad {
NSLog(#"loading");
GDataServiceGoogleYouTube *service = [self youTubeService];
NSString *uploadsID = kGDataYouTubeUserFeedIDUploads;
NSURL *feedURL = [GDataServiceGoogleYouTube youTubeURLForUserID:#"BmcCarmen"
userFeedID:uploadsID];
[service fetchFeedWithURL:feedURL
delegate:self
didFinishSelector:#selector(request:finishedWithFeed:error:)];
[super viewDidLoad];
}
- (void)request:(GDataServiceTicket *)ticket
finishedWithFeed:(GDataFeedBase *)aFeed
error:(NSError *)error {
self.feed = (GDataFeedYouTubeVideo *)aFeed;
[self.tableView reloadData];
}
#pragma mark Table view methods
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [[feed entries] count];
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
// Configure the cell.
GDataEntryBase *entry = [[feed entries] objectAtIndex:indexPath.row];
NSString *title = [[entry title] stringValue];
NSArray *thumbnails = [[(GDataEntryYouTubeVideo *)entry mediaGroup] mediaThumbnails];
cell.textLabel.text = title;
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:[[thumbnails objectAtIndex:0] URLString]]];
cell.imageView.image = [UIImage imageWithData:data];
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 100.0f;
}
- (void)dealloc {
[super dealloc];
}
- (GDataServiceGoogleYouTube *)youTubeService {
static GDataServiceGoogleYouTube* _service = nil;
if (!_service) {
_service = [[GDataServiceGoogleYouTube alloc] init];
[_service setShouldCacheDatedData:YES];
[_service setServiceShouldFollowNextLinks:YES];
}
// fetch unauthenticated
[_service setUserCredentialsWithUsername:nil
password:nil];
return _service;
}
#end
GCD is a wonderful thing. Here is what I do:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"youTubeCell";
UITableViewCell *cell = nil;
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Configure the cell.
GDataEntryBase *entry = [[feed entries] objectAtIndex:indexPath.row];
NSString *title = [[entry title] stringValue];
NSArray *thumbnails = [[(GDataEntryYouTubeVideo *)entry mediaGroup] mediaThumbnails];
cell.textLabel.text = title;
// Load the image with an GCD block executed in another thread
dispatch_queue_t downloadQueue = dispatch_queue_create("image downloader", NULL);
dispatch_async(downloadQueue, ^{
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:[[thumbnails objectAtIndex:0] URLString]]];
UIImage * image = [UIImage imageWithData:data];
dispatch_async(dispatch_get_main_queue(), ^{
cell.imageView.image = image;
cell.imageView.contentMode = UIViewContentModeScaleAspectFit;
[cell setNeedsLayout];
});
});
dispatch_release(downloadQueue);
return cell;
}
Scrolling is now super smooth. The only drawback is that when you scroll quickly, cells are re-used and sometimes the images change as new ones come in.

xcode may not respond to warning

Can't seem to get rid of a warning. The warning is:
'UIImage' may not respond to '-scaleToSize'
above the #implmentation MyViewController I have this #implementation:
#implementation UIImage (scale)
-(UIImage*)scaleToSize:(CGSize)size
{
UIGraphicsBeginImageContext(size);
[self drawInRect:CGRectMake(0, 0, size.width, size.height)];
UIImage* scaledImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return scaledImage;
}
#end
Then I have MyViewController implementation
#implementation TodayNewsTableViewController
#synthesize dataList;
......
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *MainNewsCellIdentifier = #"MainNewsCellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: MainNewsCellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier: MainNewsCellIdentifier] autorelease];
}
NSUInteger row = [indexPath row];
NSDictionary *stream = (NSDictionary *) [dataList objectAtIndex:row];
NSString *title = [stream valueForKey:#"title"];
if( ! [title isKindOfClass:[NSString class]] )
{
cell.textLabel.text = #"";
}
else
{
cell.textLabel.text = title;
}
cell.textLabel.numberOfLines = 2;
cell.textLabel.font =[UIFont systemFontOfSize:10];
cell.detailTextLabel.numberOfLines = 1;
cell.detailTextLabel.font= [UIFont systemFontOfSize:8];
cell.detailTextLabel.text = [stream valueForKey:#"created"];
NSString *i = [NSString stringWithFormat:#"http://www.mywebsite.co.uk/images/%#", [stream valueForKey:#"image"]];
NSData *imageURL = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:i]];
UIImage *newsImage = [[UIImage alloc] initWithData:imageURL] ;
UIImage *scaledImage = [newsImage scaleToSize:CGSizeMake(50.0f, 50.0f)]; // warning is appearing here.
cell.imageView.image = scaledImage;
[imageURL release];
[newsImage release];
return cell;
}
Thanks for your time in advance.
Frames
To avoid this warning compiler must "see" your custom method declaration. So you should put
#interface UIImage (scale)
-(UIImage*)scaleToSize:(CGSize)size
#end
somewhere - either to corresponding header file, or in the same implementation file if you do not want this method to be accessible outside current file.

Resources