I'm developing and iOS app and my UITableView is kinda slow. I would like to know all the possible reasons for a slow scroll.
Note that I'm using prototype cells in the storyboard and I don't have any images.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *identifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil)
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
Classification *classification = [classifications objectAtIndex:indexPath.row];
UILabel *teamLabel = (UILabel *) [cell viewWithTag:101];
[teamLabel setText:[classification team]];
[cell setBackgroundColor:[UIColor clearColor]];
return cell;
}
Also, it runs smoothly in the simulator.
I'm using an iPad 2.
Download the data asynchronously in your:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
Use 2 separated UITableViewCell objects, one to fill with text data, one for images which need to be in thread such as dispatch_async, example from my project:
if (imageUrl.length > 0)
{
NSString *completeURL = #"";
completeURL = [completeURL stringByAppendingString:kPROFILE_IMAGE_URL];
completeURL = [completeURL stringByAppendingString:#"/2/"];
completeURL = [completeURL stringByAppendingString:imageUrl];
completeURL = [completeURL stringByAppendingString:#".png"];
NSString *fileName = [NSString stringWithFormat:#"%#.png", imageUrl];
NSString* path = GetMediaFolder();
BOOL imageExists = [[NSFileManager defaultManager] fileExistsAtPath:[path stringByAppendingPathComponent:fileName]];
if (imageExists)
{
// NSLog(#"Image exists");
// Update UI
dispatch_async(dispatch_get_main_queue(), ^
{
UITableViewCell *updateCell = (id)[tableView cellForRowAtIndexPath:indexPath];
UIImageView *imageView = (UIImageView*)[updateCell viewWithTag:kTAG_IMAGE];
imageView.image=[UIImage imageWithContentsOfFile:[path stringByAppendingPathComponent:fileName]];
imageView.layer.cornerRadius = 23.0;
imageView.layer.borderColor = (__bridge CGColorRef)([UIColor clearColor]);
imageView.layer.borderWidth = 1.0;
imageView.clipsToBounds = YES;
imageView.contentMode = UIViewContentModeScaleAspectFill;
[updateCell setNeedsLayout];
});
}
else
{
// NSLog(#"Image not exists");
// Download The Image
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^
{
NSURL *imageURL=[NSURL URLWithString:completeURL];
NSData *image=[NSData dataWithContentsOfURL:imageURL];
[image writeToFile:[path stringByAppendingPathComponent:fileName] atomically:YES];
// Update UI
dispatch_async(dispatch_get_main_queue(), ^
{
UITableViewCell *updateCell = (id)[tableView cellForRowAtIndexPath:indexPath];
UIImageView *imageView = (UIImageView*)[updateCell viewWithTag:kTAG_IMAGE];
imageView.image=[UIImage imageWithContentsOfFile:[path stringByAppendingPathComponent:fileName]];
imageView.layer.cornerRadius = 23.0;
imageView.layer.borderColor = (__bridge CGColorRef)([UIColor clearColor]);
imageView.layer.borderWidth = 1.0;
imageView.clipsToBounds = YES;
imageView.contentMode = UIViewContentModeScaleAspectFill;
[updateCell setNeedsLayout];
});
});
}
}
}
return cell;
EDIT 1:
I see you don't use images, you still need to download all the data asynchronously and fill it in the new UITableViewCell when download finished (instead of my images downloads, temporary data and downloaded data).
Related
This is throwing an error. It is my first time trying to do this part, so bear with me.
My error is at cell.image.frame...:
- (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];
//if(group[PF_GROUP_LOGO] != nil){
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.layer.cornerRadius = cell.image.frame.size.width/2;
cell.layer.masksToBounds = YES;
cell.imageView.image = image;
cell.textLabel.text = group[PF_GROUP_NAME];
cell.detailTextLabel.text = [NSString stringWithFormat:#"%d users", (int) [group[PF_GROUP_MEMBERS] count]];
cell.detailTextLabel.textColor = [UIColor lightGrayColor];
});
});
return cell;
}
I figured it out! I was just referencing cell. in an inappropriate way.
//-------------------------------------------------------------------------------------------------------------------------------------------------
- (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];
//if(group[PF_GROUP_LOGO] != nil){
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.imageView.layer.cornerRadius = cell.imageView.frame.size.height /2;
cell.imageView.layer.masksToBounds = YES;
cell.imageView.layer.borderWidth = 0;
cell.textLabel.text = group[PF_GROUP_NAME];
cell.detailTextLabel.text = [NSString stringWithFormat:#"%d users", (int) [group[PF_GROUP_MEMBERS] count]];
cell.detailTextLabel.textColor = [UIColor lightGrayColor];
});
});
return cell;
}
Currently I'm making a Messaging App but recently the tableCells are not resizing as expected. I've used the new iOS 8 relative height functionality but still nothing changes. All the design it's being done via Storyboard so far everything works as expected but need to tell the tableCell to resize based on the textview height. here how it's currently looking.
http://i.imgur.com/lqoxxJV.png
The Code I'm using is the following.
- (UITableViewCell*)tableView:(UITableView*)table cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDictionary* chatMessage = [_conversation objectAtIndex:indexPath.row];
// Show If type is String
if ([chatMessage[#"type"] isEqualToString:#"string"]) {
// if its me
if([chatMessage[#"user_sender"] isEqualToString:_user_sender]){
NSString *CellIdentifier = #"fromMe";
FromMeTableViewCell *cell = [table dequeueReusableCellWithIdentifier:CellIdentifier];
[cell.message_me setNeedsLayout];
[cell.message_me layoutIfNeeded];
cell.message_me.clipsToBounds = YES;
cell.message_me.textContainerInset = UIEdgeInsetsMake(8.0, 8.0, 7.0, 8.0);
cell.message_me.text = chatMessage[#"msg"];
cell.message_date_me.text = [NSString stringWithFormat:#"%#", chatMessage[#"message_date"]];
cell.avatar_message_me.clipsToBounds = YES;
cell.avatar_message_me.layer.cornerRadius = cell.avatar_message_me.frame.size.width /2;
cell.avatar_message_me.image = [UIImage imageNamed:#"amber.png"];
return cell;
}
// it its other user
else {
NSString *CellIdentifier = #"fromThem";
FromThemTableViewCell *cell = [table dequeueReusableCellWithIdentifier:CellIdentifier];
[cell.message_them setNeedsLayout];
[cell.message_them layoutIfNeeded];
cell.message_them.clipsToBounds = YES;
cell.message_them.textContainerInset = UIEdgeInsetsMake(8.0, 8.0, 7.0, 8.0);
cell.message_them.text = chatMessage[#"msg"];
cell.message_date_them.text = [NSString stringWithFormat:#"%#", chatMessage[#"message_date"]];
cell.avatar_message_them.clipsToBounds = YES;
cell.avatar_message_them.layer.cornerRadius = cell.avatar_message_them.frame.size.width /2;
cell.avatar_message_them.image = [UIImage imageNamed:#"jenny.png"];
return cell;
}
}
// Show if type is Image File
else if ([chatMessage[#"type"] isEqualToString:#"img"]){
// if its me
if(![chatMessage[#"user_sender"] isEqualToString:_user_sender]){
NSString *CellIdentifier = #"fromThemImage";
FromThemImageTableCell *cell = [table dequeueReusableCellWithIdentifier:CellIdentifier];
cell.image_type_them.image = [UIImage imageNamed:#"foto.jpeg"];
cell.message_date_them.text = [NSString stringWithFormat:#"%#", chatMessage[#"message_date"]];
return cell;
}
// if its other user
else {
NSString *CellIdentifier = #"fromMeImage";
FromMeImageTableCell *cell = [table dequeueReusableCellWithIdentifier:CellIdentifier];
cell.image_type_me.image = [UIImage imageNamed:#"foto.jpeg"];
cell.message_date_me.text = [NSString stringWithFormat:#"%#", chatMessage[#"message_date"]];
return cell;
}
}
else if ([chatMessage[#"type"] isEqualToString:#"file"]){
NSLog(#"Type: %#", chatMessage[#"type"]);
NSString *CellIdentifier = #"fromThemFile";
FromThemFileTableCell *cell = [table dequeueReusableCellWithIdentifier:CellIdentifier];
cell.message_date_them.text = [NSString stringWithFormat:#"%#", chatMessage[#"message_date"]];
cell.file_name_them.text = chatMessage[#"msg"];
cell.file_type_them.contentMode = UIViewContentModeScaleAspectFill;
NSArray *formats = [chatMessage[#"msg"] componentsSeparatedByString:#"."];
// PDF Format
if ([formats[1] isEqualToString:#"pdf"]) {
cell.file_type_them.image = [UIImage imageNamed:#"pdf.png"];
}
// Word Format
if ([formats[1] isEqualToString:#"doc"]) {
cell.file_type_them.image = [UIImage imageNamed:#"doc.png"];
}
if ([formats[1] isEqualToString:#"docx"]) {
cell.file_type_them.image = [UIImage imageNamed:#"doc.png"];
}
// Power Point Format
if ([formats[1] isEqualToString:#"pptx"]) {
cell.file_type_them.image = [UIImage imageNamed:#"ppt.png"];
}
if ([formats[1] isEqualToString:#"ppt"]) {
cell.file_type_them.image = [UIImage imageNamed:#"ppt.png"];
}
// Excel Format
if ([formats[1] isEqualToString:#"xls"]) {
cell.file_type_them.image = [UIImage imageNamed:#"xls.png"];
}
if ([formats[1] isEqualToString:#"xlsx"]) {
cell.file_type_them.image = [UIImage imageNamed:#"xls.png"];
}
return cell;
}
else {
// Remember to set this as a default value
NSString *CellIdentifier = #"fromThem";
FromThemTableViewCell *cell = [table dequeueReusableCellWithIdentifier:CellIdentifier];
cell.message_them.text = chatMessage[#"msg"];
cell.message_date_them.text = [NSString stringWithFormat:#"%#", chatMessage[#"message_date"]];
return cell;
}
}
The constraints I've been using for the textview to be able to expand and look like this is the following.
http://i.imgur.com/Kjva3ES.png
Any Idea how to fix this on a decent way..?
If your UITableViewCells vary in height due to variations in content you need to implement
- (CGFloat)tableView:(UITableView *)tableView
heightForRowAtIndexPath:(NSIndexPath *)indexPath;
to tell UITableView the height for each row.
In one of my apps this mainly involved calling
- (CGRect)boundingRectWithSize:(CGSize)size options:(NSStringDrawingOptions)options attributes:(NSDictionary *)attributes context:(NSStringDrawingContext *)context;
to predict the area needed by my expanding text field, and then adding in some margin.
I am facing image repeating issue, when try to scroll to downside or upside if image not loading completed then image repeat the same as complete the image downloaded. Here is my code:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"galCustom";
GTInstaTableViewCell *cell = (GTInstaTableViewCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"GTInstaTableViewCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
cell.selectionStyle = UITableViewCellSelectionStyleNone;
for (UIView *view in cell.imageView.subviews)
{
[view removeFromSuperview];
}
cell._imageLoadingView.hidden = NO;
[cell._imageLoadingView startAnimating];
}
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
GTInstaTableViewCell *cell1 = (GTInstaTableViewCell *)[tableView cellForRowAtIndexPath:indexPath];
//Gallery Photo
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),^
{
cell.imageView.image = nil;
NSString *strImgname = [[arrTabledata objectAtIndex:indexPath.row] objectForKey:#"photo_name"];
imgPname = [IMAGE_URL stringByAppendingString:strImgname];
NSString *aPath = [self.thumbDirectory stringByAppendingPathComponent:strImgname];
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:aPath])
{
NSURL *url = [NSURL URLWithString:imgPname];
NSData *imgData = [[NSData alloc] initWithContentsOfURL:url];
float containerWidth = 648.0;
//Image Resize
UIImage *image = [UIImage imageWithData:imgData];
float width = image.size.width;
float height= image.size.height;
float thumbWidth = width/containerWidth;
float imageHeight = height/thumbWidth;
CGSize sacleSize = CGSizeMake(containerWidth, imageHeight);
UIGraphicsBeginImageContextWithOptions(sacleSize, NO, 0.0);
[image drawInRect:CGRectMake(0, 0, sacleSize.width, sacleSize.height)];
UIImage *resizedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
NSData *imageData = UIImagePNGRepresentation(resizedImage);
[imageData writeToFile:aPath atomically:YES];
}
UIImage *image = [UIImage imageWithContentsOfFile:aPath];
dispatch_async(dispatch_get_main_queue(), ^
{
if (image)
{
cell.imageView.image = image;
cell.imageView.contentMode = UIViewContentModeScaleAspectFit;
cell.imageView.clipsToBounds = YES;
[cell1._imageLoadingView stopAnimating];
cell1._imageLoadingView.hidden = YES;
}
else
{
cell.imageView.image = [UIImage imageNamed:#"noImage.jpg"];
}
});
});
}
You should implement the GTInstaTableViewCell.m method:
- (void) prepareForReuse
{
self.imageView.image = nil;
}
//UITableView
NSString *data_image;
data_image = [[message_array objectAtIndex:indexPath.row] valueForKey:#"img_thumb"];
NSLog(#"data_image---%#",data_image);
if (![data_image isEqualToString:#""])
{
img_bubble = [[UIImageView alloc]initWithImage:cloudImage];
[img_bubble setFrame:CGRectMake(225, 0,85 , 80)];
img_buble_sender = [[UIImageView alloc]initWithImage:cloudImage1];
[img_buble_sender setFrame:CGRectMake(10,0,90,80)];
urlImage = [NSURL URLWithString:[[message_array objectAtIndex:indexPath.row] valueForKey:#"image"]];
asyncImage = [[AsyncImageView alloc]initWithFrame:CGRectMake(238, 10, 55, 55)];
[asyncImage loadImageFromURL:urlImage];
//images1=[[UIImageView alloc]initWithFrame:CGRectMake(238, 10,55 , 55)];
//[images1 setImageWithURL:urlImage];
}
Try this in your cellForRowAtIndexPath:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
//create new cell
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
//common settings
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
cell.imageView.contentMode = UIViewContentModeScaleAspectFill;
cell.imageView.frame = CGRectMake(238, 10, 55, 55);
cell.imageView.clipsToBounds = YES;
}
else {
//cancel loading previous image for cell
[[AsyncImageLoader sharedLoader] cancelLoadingImagesForTarget:cell.imageView];
}
//set placeholder image or cell won't update when image is loaded
cell.imageView.image = [UIImage imageNamed:#"Placeholder.png"];
//load the image
cell.imageView.imageURL = [NSURL URLWithString:[[message_array objectAtIndex:indexPath.row];
return cell;
}
I'm not sure about which AsyncImageView you are using, but you could try this:
//UITableView
NSString *data_image;
data_image = [[message_array objectAtIndex:indexPath.row] valueForKey:#"img_thumb"];
NSLog(#"data_image---%#",data_image);
if (![data_image isEqualToString:#""])
{
img_bubble = [[UIImageView alloc]initWithImage:cloudImage];
[img_bubble setFrame:CGRectMake(225, 0,85 , 80)];
img_buble_sender = [[UIImageView alloc]initWithImage:cloudImage1];
[img_buble_sender setFrame:CGRectMake(10,0,90,80)];
urlImage = [NSURL URLWithString:[[message_array objectAtIndex:indexPath.row] valueForKey:#"image"]];
asyncImage = [[AsyncImageView alloc]initWithFrame:CGRectMake(238, 10, 55, 55)];
[asyncImage loadImageFromURL:urlImage];
[cell.contentView addSubview:asyncImage];
}
Having trouble with the asynchronous images don't know why having this weird problem. I am applying the Asynchronous images load technique but the images are not freezing once loaded on scrolling they are again loading.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell=[self getCellContentView:CellIdentifier];
}
[cell setBackgroundColor:[UIColor redColor]];
NSLog(#"%i",[discussionArray count]);
NSDictionary *dict=[discussionArray objectAtIndex:indexPath.row];
UILabel *textLabel1 = (UILabel *)[cell viewWithTag:1];
UILabel *textLabel2 = (UILabel *)[cell viewWithTag:2];
UILabel *textLabel3 = (UILabel *)[cell viewWithTag:3];
UIImageView *avatarimage = (UIImageView *)[cell viewWithTag:4];
UIImageView *new_oldimage = (UIImageView *)[cell viewWithTag:5];
UIImageView *avatarimage_cover = (UIImageView *)[cell viewWithTag:6];
textLabel1.text =[dict objectForKey:#"user"];
textLabel2.text =[dict objectForKey:#"date"];
textLabel3.text =[dict objectForKey:#"text"];
NSString *photoStrn=[dict objectForKey:#"photo"];
avatarimage.image = nil;
dispatch_async(dispatch_get_global_queue(0,0), ^{
NSString *u=[NSString stringWithFormat:#"http://%#",photoStrn];
NSURL *imageURL=[NSURL URLWithString:u];
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
dispatch_sync(dispatch_get_main_queue(), ^{
UIImage *dpImage = [UIImage imageWithData:imageData];
if (dpImage==nil)
{
dpImage = [UIImage imageNamed:#"profileImage.png"];
}
[UIView animateWithDuration:5.0 animations:^{
avatarimage.alpha = 1.0;
}];
avatarimage.image = dpImage;
});
});
avatarimage_cover.image = [UIImage imageNamed:#"avtrcover.png"];
NSString *new=[dict objectForKey:#"new"];
if ([new isEqualToString:#"0"])
{
new_oldimage.image = [UIImage imageNamed:#"old_message.png"];
}
else
{
new_oldimage.image = [UIImage imageNamed:#"new_message.png"];
}
textLabel3.numberOfLines = 0;
NSString *detailText = [[discussionArray objectAtIndex:indexPath.row] objectForKey:#"text"];
CGSize textSize = [detailText sizeWithFont:[UIFont boldSystemFontOfSize:13] constrainedToSize:CGSizeMake(240, MAXFLOAT) lineBreakMode:UILineBreakModeWordWrap];
textLabel3.frame = CGRectMake(70, 53, 240, textSize.height+detailTextoffset );
return cell;
}
You are setting avatarimage.image to nil each time the cell is configured. You call the loading code regardless what. Thus, it is not surprising you keep reloading the image.
Instead, you should maintain a mutable array of downloaded images. This could be just file URLs for the images somewhere in the documents folder. If the image exists use it, if not download it.
if (imageArray[indexPath.row]) {
// use the image to populate the image view
}
else {
// fetch async from server
}
this will solve you problem completely. click on below link:
Image on top of Cell