I have a question about table view cells.
In cell I have an UIImageView. This view loads from web service with getter:
- (UIImageView *)imageView
{
if (!_ imageView)
{
_imageView = [[TurnipImageView alloc] init];
_imageView.backgroundColor = [UIColor clearColor];
}
[_imageView loadImageViewFromWebService];
return _imageView;
}
When I add [_imageView loadImageViewFromWebService]; call inside of if (!_ imageView) statement, image view is not loaded correctly.
When I scroll table view, cells are reloading as well as image views and causing lags in scrolling.
Maybe anyone knows how to optimise this process?
You should try lazy loading of the table view :
here is the sample : https://developer.apple.com/library/ios/samplecode/LazyTableImages/Introduction/Intro.html
also have a look at this :
Lazy loading UITableView with multiple images in each cell
Try this
dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), ^{
//perform download on background thread
dispatch_async(dispatch_get_main_queue(), ^{
//assign image to imageview on main thread, UI operations should be carried on main thread
});
});
- (UIImageView *)imageView
{
if (!_ imageView)
{
_imageView = [[TurnipImageView alloc] init];
_imageView.backgroundColor = [UIColor clearColor];
}
dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), ^{
//perform download on background thread
[_imageView loadImageViewFromWebService];
});
return _imageView;
}
Hope this helps
Related
I have implemented one row-multiple column collectionview which stays at the top of the HomeViewController.
Initially, I want to set a default item as being selected and highlighted with a red line color beneath and also as well as text color red, refer to the image as follows.
For some reason(s), it shows multiple category items have red line colors beneath which is not what I expected, but text colors are correct. I do not know what I am doing wrong?
CategoryCollectionViewCell.m
#implementation CategoryCollectionViewCell
#synthesize categoryLabel,highlightedLabel;
- (void)setSelected:(BOOL)selected
{
if(selected)
{
self.categoryLabel.textColor = [UIColor redColor];
self.highlightedLabel.backgroundColor = [UIColor redColor];
}
else
{
self.categoryLabel.textColor = [UIColor lightGrayColor];
self.highlightedLabel.backgroundColor = [UIColor clearColor];
}
}
#end
HomeViewController.m
-(void) viewWillAppear: (BOOL) animated {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^(void) {
[self loadFromURL]; // that is not related with categoryCollectionView
dispatch_async(dispatch_get_main_queue(), ^{
[self.categoryCollectionView selectItemAtIndexPath:[NSIndexPath indexPathForRow:3 inSection:0] animated:YES scrollPosition:UICollectionViewScrollPositionCenteredHorizontally];
}
});
}
You didn't post all relevant code here.
From your code there may be reason :
1) Your highlighted label color is set to default red in cell. When you pass your code changes that cell only.
For this set your highlighted label background color clear in xib/storyboard.
I have a UICollectionView displaying a bunch of images. If I don't load the images asynchronously the scrolling is very choppy and provides a poor user experience. When I load the images asynchronously the scrolling is smooth but it takes a good 5 to 10 seconds to load each image.
Why does it take so long for images to appear when loaded in the background? Here is my code for the background thread which is inside of the cellForItemAtIndexPath delegate:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
UIImageView *bg = (id)self.backgroundView;
UIImageView *selbg = (id)self.selectedBackgroundView;
if (![bg isKindOfClass:[UIImageView class]])
bg = [[UIImageView alloc] initWithImage:thumb];
else
[bg setImage:thumb];
if (![selbg isKindOfClass:[UIImageView class]]){
selbg = [[UIImageView alloc] initWithImage:thumb];
coloroverlay = [[UIView alloc] initWithFrame:selbg.bounds];
[coloroverlay setAutoresizingMask:UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight];
[selbg addSubview:coloroverlay];
} else
[selbg setImage:thumb];
[bg setContentMode:UIViewContentModeScaleAspectFill];
[bg setTag: 1];
[coloroverlay setBackgroundColor:[col colorWithAlphaComponent:0.33f]];
[selbg setContentMode:UIViewContentModeScaleAspectFill];
dispatch_sync(dispatch_get_main_queue(), ^{
[self setBackgroundView:bg];
[self setSelectedBackgroundView:selbg];
});
});
EDIT: As #geraldWilliam pointed out, I shouldn't be accessing views from the secondary thread. Here is what I have updated my code to and fixed the issue of images getting set to the wrong cell:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
UIImageView *bg = (id)self.backgroundView;
UIImageView *selbg = (id)self.selectedBackgroundView;
if (![bg isKindOfClass:[UIImageView class]]) bg = [[UIImageView alloc] initWithImage:thumb];
if (![selbg isKindOfClass:[UIImageView class]]){
selbg = [[UIImageView alloc] initWithImage:thumb];
coloroverlay = [[UIView alloc] initWithFrame:selbg.bounds];
[coloroverlay setAutoresizingMask:UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight];
[selbg addSubview:coloroverlay];
}
dispatch_sync(dispatch_get_main_queue(), ^{
[bg setImage:thumb];
[selbg setImage:thumb];
[bg setContentMode:UIViewContentModeScaleAspectFill];
[bg setTag: 1];
[coloroverlay setBackgroundColor:[col colorWithAlphaComponent:0.33f]];
[selbg setContentMode:UIViewContentModeScaleAspectFill];
[self setBackgroundView:bg];
[self setSelectedBackgroundView:selbg];
});
});
Most of the code you have here is fine for the main queue. The loading of the image should be on a global queue, but the rest, especially setting the image view's image, should be on the main queue. What's going on in your code is that you're dispatching back to the main queue to set the background view but leaving the assignment of the image property in the background. So, try something like:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:myImageURL]];
dispatch_sync(dispatch_get_main_queue(), ^{
imageView.image = image;
[self setBackgroundView:imageView];
});
});
I strongly recommend watching WWDC 2012 Session 211: Building Concurrent User Interfaces on iOS, which is the best WWDC session ever. It’s full of clearly presented, practical advice.
Doing stuff with UIImageView off the main queue is worrying and should be fixed, but is probably not the cause of slowness. You haven’t showed us where thumb comes from, which is likely the slow bit.
This my collectionView cellForItemAtIndexPath method:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
ChessBoard *boardView = [[ChessBoard alloc] initWithFrame:CGRectMake(0, cell.frame.size.height - cell.frame.size.width, cell.frame.size.width, cell.frame.size.width)];
boardView.fenString = self.fenArray[indexPath.item];
[boardView.highlightBoxes addObject:self.lastMoveFromSqiArray[indexPath.item]];
[boardView.highlightBoxes addObject:self.lastMoveToSqiArray[indexPath.item]];
NSLog(#"from %#, to %#", self.lastMoveFromSqiArray[indexPath.item], self.lastMoveToSqiArray[indexPath.item]);
boardView.showCoordinates = NO;
[boardView baseInit];
//main thread
dispatch_async(dispatch_get_main_queue(), ^{
[cell addSubview:boardView];
});
});
cell.layer.shouldRasterize = YES;
cell.layer.rasterizationScale = [UIScreen mainScreen].scale;
return cell;
ChessBoard is a UIView subclass which returns a uiview with chessboard drawn in it.
When I run this, the view controller is loaded without any content in cells and it takes too much time to load cells.
Is there any way i can make it better ?
I have created collection view programmatically where i have created UIImageview programmatically within collectionViewCell and images are displayed in imageview are downloaded from server asynchronously.
Code below is written in cellForItemAtIndexPath: method -
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
NSURL *imageURL = [NSURL URLWithString:[arrmImgPaths objectAtIndex:indexPath.row]];
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
UIImage *image = [UIImage imageWithData:imageData];
// [imgvMainView removeFromSuperview];
// Now the image will have been loaded and decoded and is ready to rock for the main thread
dispatch_sync(dispatch_get_main_queue(), ^{
imgvMainView =[[UIImageView alloc] initWithFrame:CGRectMake(34,41,177,124)];
[cell addSubview:imgvMainView];
[imgvMainView setImage:image];
imgvMainView.contentMode = UIViewContentModeScaleAspectFit;
});
});
Problem is, when i scroll collection view some images are overlapped on one another. Please tell me solution to avoid it.
Thanks in advance.
Please use below line I hope this would work what I am assuming problem of duplicate cell.
UIImageView* imgvMainView =[[UIImageView alloc] initWithFrame:CGRectMake(34,41,177,124)];
[cell.contentView addSubview:imgvMainView];
This is most likely happening because you are reusing cells (correctly) and that the previous use of the cell is still calling the previous image. You must cancel the async task when the cell goes out of view. By the way, it will make you life much easier to use AFNetworking+UIImageView to do this.
One other option is to check that there is no UIimageView in your cell before you create one.
dispatch_sync(dispatch_get_main_queue(), ^{
UIView *viewToRemove;
for (UIView *view in cell.subViews) {
if (view isKingOfClass:UIImageView){
viewToRemove = view;
}
}
[viewToRemove removeFromSuperView];
imgvMainView =[[UIImageView alloc] initWithFrame:CGRectMake(34,41,177,124)];
[cell addSubview:imgvMainView];
[imgvMainView setImage:image];
imgvMainView.contentMode = UIViewContentModeScaleAspectFit;
});
I have added this UICollectionViewDelegate with my implementation:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
SAPCollectionViewCell *collectionViewCell = (SAPCollectionViewCell *)[collectionView dequeueReusableCellWithReuseIdentifier:#"SAPCollectionViewCell" forIndexPath:indexPath];
NSInteger index = indexPath.item + indexPath.section * 2;
NSString *imagePath = [_images objectAtIndex:index];
collectionViewCell.index = index;
[collectionViewCell setImageWithPath:imagePath];
collectionViewCell.delegateCell = self;
return collectionViewCell;
}
In my custom cell I have this method:
- (void)setImageWithPath:(NSString *)imagePath
{
if (!self.imageView)
{
CGRect rect = self.contentView.frame;
self.imageView = [[UIImageView alloc] initWithFrame:rect];
[self.contentView addSubview:self.imageView];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
UIImage *image = [UIImage imageFromPath:imagePath];
UIImage *resizedImage = [UIImage imageWithImage:image scaledToWidth:rect.size.width];
dispatch_async(dispatch_get_main_queue(), ^{
[self.imageView setImage:resizedImage];
});
});
}
}
So as you can see if the cell does not have UIImageView then I start to create it and in background I get image from local path and resize it to cell content view width, then in main queue I set image to UIImageView.
This part work good but when I try to scroll my UICollectionView e.g. with 10 images I noticed that for example if first 2 top images were disappeared when I scroll down and then when I scroll back to the top, the images are changed placed.
This is state of images before scrolling down:
And this state after I scroll UIColectionView back to the top:
So as you can see first to item changed theirs location. As I have set if (!self.imageView) in my code above it should means that the image never will be created twice or more times.
I opened debugger and was checking this method:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
So the images paths returned one by one as it worked when UICollectionView just created cells at first time.
At first time first UICollectionViewCell has 0x8fbc1d0
and second has 0x8d0a4c0 address
But when I scroll down and then scroll up the debugger shown me second address at first 0x8d0a4c0 and then shown me 0x8fbc1d0
But I can't understand why items change theirs order. Or maybe here is another issue?
Also I don't have any method in the code that make for example for me reorder for cell. just few UICollection view methods delegate that configure count of cells and create them.
Also if I remove if (!self.imageView) seems everything works good, but then my code every time invoke dispatch that's no good for me, because when I resize image to the cell size I don't need do it twice.
When you scroll the two items off screen, your CollectionView puts the associated cells back onto the 'queue'. When you scroll back up again, the dequeueResuableCellwithReuseIdentifier in cellForItemAtIndexPath retrieves them from that queue, but (as you've seen) not necessarily in the same order. Thus the cell which was item 0 has been used for item 1 and vice versa. As you've seen, the if (!self.imageView) actually stops the correct image from being loaded. If you want to avoid retrieving and resizing them each time, then I would store the (resized) imageViews in an array.
- (void)setImageWithPath:(NSString *)imagePath
{
if (!self.imageView)
{
CGRect rect = self.contentView.frame;
self.imageView = [[UIImageView alloc] initWithFrame:rect];
[self.contentView addSubview:self.imageView];
}
if (![self.imagePath isEqualsToString:imagePath] && self.imageView.image == nil)
{
self.imagePath = imagePath;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
UIImage *image = [UIImage imageFromPath:imagePath];
UIImage *resizedImage = [UIImage imageWithImage:image scaledToWidth:rect.size.width];
dispatch_async(dispatch_get_main_queue(), ^{
if ([self.imagePath isEqualsToString:imagePath])
{
[self.imageView setImage:resizedImage];
}
});
});
}
}