xcode iphone - jerky scroll UITableView CellForRowAtIndexPath - xcode

Almost sorted with my 1st app, just a simple news app but when I load it onto my iPhone the scroll seems jerky can someone have a look at my function and see if i'm doing something wrong.
I need the image on the right hand side thats why i'm using custom cells.
Thanks
For any help
#define DATELABEL_TAG 1 #define MAINLABEL_TAG 2 #define PHOTO_TAG 3
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *MainNewsCellIdentifier = #"MainNewsCellIdentifier";
UILabel *mainLabel, *dateLabel;
UIImageView *photo;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: MainNewsCellIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier: MainNewsCellIdentifier] autorelease];
cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
dateLabel = [[[UILabel alloc] initWithFrame:CGRectMake(15.0,15.0,170.0,15.0)] autorelease];
dateLabel.tag = DATELABEL_TAG;
dateLabel.font = [UIFont systemFontOfSize:10.0];
dateLabel.textAlignment = UITextAlignmentLeft;
dateLabel.textColor = [UIColor darkGrayColor];
dateLabel.autoresizingMask = UIViewAutoresizingFlexibleRightMargin; //| UIViewAutoresizingFlexibleHeight;
[cell.contentView addSubview:dateLabel];
mainLabel = [[[UILabel alloc] initWithFrame:CGRectMake(15.0,28.0,170.0,60.0)] autorelease];
mainLabel.tag = MAINLABEL_TAG;
mainLabel.font = [UIFont boldSystemFontOfSize:14.0];
mainLabel.textColor = [UIColor blackColor];
mainLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleRightMargin;
mainLabel.numberOfLines = 0;
//mainLabel.backgroundColor = [UIColor greenColor];
[cell.contentView addSubview:mainLabel];
photo = [[[UIImageView alloc] initWithFrame:CGRectMake(190.0,15.0,85.0,85.0)] autorelease];
photo.tag = PHOTO_TAG;
photo.contentMode = UIViewContentModeScaleAspectFit;//UIViewContentModeScaleAspectFit; //
[cell.contentView addSubview:photo];
}
else {
dateLabel = (UILabel *)[cell.contentView viewWithTag:DATELABEL_TAG];
mainLabel = (UILabel *)[cell.contentView viewWithTag:MAINLABEL_TAG];
photo = (UIImageView *)[cell.contentView viewWithTag:PHOTO_TAG];
}
NSUInteger row = [indexPath row];
NSDictionary *stream = (NSDictionary *) [dataList objectAtIndex:row];
NSString *title = [stream valueForKey:#"title"];
NSString *titleString = #"";
if( ! [title isKindOfClass:[NSString class]] )
{
titleString = #"";
}
else
{
titleString = title;
}
CGSize maximumSize = CGSizeMake(180, 9999);
UIFont *dateFont = [UIFont fontWithName:#"Helvetica" size:14];
CGSize dateStringSize = [titleString sizeWithFont:dateFont
constrainedToSize:maximumSize
lineBreakMode:mainLabel.lineBreakMode];
CGRect dateFrame = CGRectMake(15.0, 28.0, 170.0, dateStringSize.height);
mainLabel.frame = dateFrame;
mainLabel.text = titleString;
dateLabel.text = [stream valueForKey:#"created"];
NSString *i = [NSString stringWithFormat:#"http://www.website.co.uk/images/%#", [stream valueForKey:#"image"]];
NSData *imageURL = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:i]];
UIImage *newsImage = [[UIImage alloc] initWithData:imageURL];
photo.image = newsImage;
[imageURL release];
[newsImage release];
return cell;
}

The problem is this:
NSString *i = [NSString stringWithFormat:#"http://www.website.co.uk/images/%#", [stream valueForKey:#"image"]];
NSData *imageURL = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:i]];
UIImage *newsImage = [[UIImage alloc] initWithData:imageURL];
You effectively say here, that as soon as the cell needs to be displayed, the image must be fetched and presented. This will cost some time - too much for a good user experience.
You should fetch the images before or while you present the table view, and cache them, e.g. in an array. Or you must handle things asynchronously, meaning that you do the loading in the background, and not wait with return cell; until the image is actually downloaded. This will be a little harder to get right.

Even if you asynchronously download images, you'll still have a jerky scroll.
Why? It's lazy image decompression. ios performs decompression at the moment it will be displayed on the screen. You have to manually decompress the images in a background thread.
Decompression does not merely mean instantiating a UIImage object. It can be somewhat complicated. The best solution, is to download SDWebImage and use the image decompressor that's included. SDWebImage will asynchronously download and perform decompression for you.
To read more about the issue see: http://www.cocoanetics.com/2011/10/avoiding-image-decompression-sickness/

Related

Using DLImageLoader-iOS

Is there anyone that can help me combine this
https://github.com/AndreyLunevich/DLImageLoader-iOS
With my code below... I am not understanding as my images are loaded from an NSString in core data and not a URL (maybe I cannot use this helper file)
My code is:
SCTableViewSection *Section3 = [self.tableViewModel sectionAtIndex:0];
Section3.cellActions.lazyLoad = ^(SCTableViewCell *cell, NSIndexPath *indexPath)
{
DLImageView *imageView = (DLImageView *)[cell viewWithTag:1];
NSString *imageString = (NSString *)[cell.boundObject valueForKey:#"thumbnail"];
NSString *fullName = [NSString stringWithFormat:#"Documents/%#", imageString];
NSString *imagePath = [NSHomeDirectory() stringByAppendingPathComponent:fullName];
UIImage *originalImage = [UIImage imageWithContentsOfFile:imagePath];
NSLog(#"added image from lazyload");
// size of actual thumbnail to display in cell
CGSize destinationSize = CGSizeMake(154,95); // size of actual thumbnail
UIGraphicsBeginImageContext(destinationSize);
[originalImage drawInRect:CGRectMake(0,0,destinationSize.width,destinationSize.height)];
UIImage *thumbnail = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[UIView transitionWithView:imageView
duration:2.25f
options:UIViewAnimationOptionTransitionCrossDissolve
animations:^{
imageView.image = thumbnail;
} completion:nil];
};

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;

Manage memory allocation issue on Map iOS

In my iPhone app I have a map view. In this I am displaying a number of pin views depends on the data from a web server. Here is the method I used for it.
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
NSString *identifier = #"myPin";
self.pinView = (MKPinAnnotationView *)[self.mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if (self.pinView == nil) {
self.pinView= [[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier]autorelease]; //11.1%
} else {
self.pinView.annotation = annotation;
}
UIButton* rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure]; //20.4%
[rightButton setTitle:annotation.title forState:UIControlStateNormal];
self.pinView.rightCalloutAccessoryView = rightButton; //2.7%
MyAnnotation *annot = (MyAnnotation*)annotation;
UIImageView *egoimageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"defaultPerson"]]; //17.5%
NSString *imageUrl = [NSString stringWithFormat:#"%#%#", CommonImageURL, [friendsProfileImageArray objectAtIndex:annot.tag]]; //9.4%
if ([[UIScreen mainScreen] respondsToSelector:#selector(displayLinkWithTarget:selector:)] &&
([UIScreen mainScreen].scale == 2.0)) {
// Retina display
[egoimageView setImageWithURL:[NSURL URLWithString:imageUrl] placeholderImage:[UIImage imageNamed:#"defaultPerson#2x.png"]]; //2.6%
egoimageView.image = [UIImage imageWithCGImage:egoimageView.image.CGImage scale:egoimageView.image.size.width/25 orientation:egoimageView.image.imageOrientation];
} else {
// non-Retina display
[egoimageView setImageWithURL:[NSURL URLWithString:imageUrl] placeholderImage:[UIImage imageNamed:#"defaultPerson.png"]]; //14.6%
egoimageView.image = [UIImage imageWithCGImage:egoimageView.image.CGImage scale:egoimageView.image.size.width/25 orientation:egoimageView.image.imageOrientation]; //1.6%
}
[egoimageView sizeToFit];
self.pinView.leftCalloutAccessoryView = egoimageView; //3.1%
[egoimageView release];
self.pinView.canShowCallout=YES;
self.pinView.animatesDrop=YES;
//pin color based on status.....
if ([annot.relationshipStatus intValue]==2 )
self.pinView.pinColor=MKPinAnnotationColorGreen;
else
self.pinView.pinColor=MKPinAnnotationColorPurple; //17.1%
return self.pinView;
}
In this I have mentioned the percentage of memory allocations. If I continuously load the map, the total memory usage increases and the app crashes. How can I properly fix this? I have tried to fix it, but I don't know what more I can do.
Please help, thanks in advance.

cocoa nsimage does not get saved

I'm new to cocoa and programming, sorry if this is something basic.
I'm using ARC. I have an NSImageView component, which is controlled by DropZone class. I drag & drop an image into it, but once I try to call in the scalling method I got, it tells me that the ": ImageIO: CGImageSourceCreateWithData data parameter is nil" I assume I'm doing something wrong, just don't know what yet.
Here's my method in DropZone.m
- (void)scaleIcons:(NSString *)outputPath{
NSImage *anImage;
NSSize imageSize;
NSString *finalPath;
anImage = [[NSImage alloc]init];
anImage = image;
imageSize = [anImage size];
imageSize.width = 512;
imageSize.height = 512;
[anImage setSize:imageSize];
finalPath = [outputPath stringByAppendingString:#"/icon_512x512.png"];
NSData *imageData = [anImage TIFFRepresentation];
NSBitmapImageRep *rep = [NSBitmapImageRep imageRepWithData:imageData];
NSData *dataToWrite = [rep representationUsingType:NSPNGFileType properties:nil];
[dataToWrite writeToFile:finalPath atomically:NO];
}
The DropZone.h
#interface DropZone : NSImageView <NSDraggingDestination>{
NSImage *image;
}
- (void)scaleIcons:(NSString *)outputPath;
#end
And here's how I call it it my AppDelegate.m
- (IBAction)createIconButtonClicked:(id)sender {
NSFileManager *filemgr;
NSString *tempDir;
filemgr = [NSFileManager defaultManager];
tempDir = [pathString stringByAppendingString:#"/icon.iconset"];
NSURL *newDir = [NSURL fileURLWithPath:tempDir];
[filemgr createDirectoryAtURL: newDir withIntermediateDirectories:YES attributes: nil error:nil];
DropZone *myZone = [[DropZone alloc] init];
[myZone scaleIcons:tempDir];
NSAlert *alert = [[NSAlert alloc] init];
[alert setMessageText:#"Done!"];
[alert runModal];
}
the image get's pulled from the pastebaord:
- (BOOL)performDragOperation:(id<NSDraggingInfo>)sender{
if ([NSImage canInitWithPasteboard:[sender draggingPasteboard]]) {
image = [[NSImage alloc]initWithPasteboard:[sender draggingPasteboard]];
[self setImage:image];
[self setImageAlignment: NSImageAlignCenter];
}
return YES;
}
For some reason my "image" gets lost. Could somebody help?
The problem is that you're creating a new instance of DropZone in your app delegate, but I'm assuming that you created the image view in IB and changed its class to DropZone. Is that correct? If so, you need to have an IBOutlet in the app delegate connected to the image view in IB, and have this:
[self.iv scaleIcons:tempDir];
where iv is my IBOutlet.
Without knowing where "image" comes from, I would say your problem is in these lines...
anImage = [[NSImage alloc]init];
anImage = image;
You should just grab the image from the imageView...
anImage = [myImageView image];

iPhone UITableViewCell slow 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!

Resources