UICollectionView willDisplayCell delegate method? - iphone

Anyway to hack into UICollectionView to call willDisplayCell delegate method, when displaying cell?
I need this for lazy loading, and I'm doing it nice with UITableView, but officially UICollectionView doesn't have that kind of delegate method.
So, any ideas? Thanks!

FYI, This has been added to the API for iOS 8:
- (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(8_0);

I'm doing something like this in cellForItemAtIndexPath: (for lazy loading images):
If my model has the image, just set the cell's image to it
If the model does not have an image, kick of an async load
When the load completes, the check if the original indexPath is still visible
If so, call cellForItemAtIndexPath for the original indexPath. Since the image now exists in the model, the cell's image will now be set.
Looks like this:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
PhotoCell * cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"PhotoCell" forIndexPath:indexPath];
id imageItem = [self.photoSet objectAtIndex:indexPath.row];
ImageManager * listingImage = (ImageManager *)imageItem;
if(listingImage.image){
cell.image = listingImage.image;
} else {
[listingImage loadImage:^(UIImage *image) {
[collectionView reloadItemsAtIndexPaths:#[indexPath]];
dispatch_async(dispatch_get_main_queue(), ^{
if ([[collectionView indexPathsForVisibleItems] containsObject:indexPath]) {
[(PhotoCell *)[collectionView cellForItemAtIndexPath:indexPath] setImage:image];
}
});
}];
}
}
return cell;
}

You can make use of SDWebImage https://github.com/rs/SDWebImage for lazy loading. You can use this effectively with UICollectionView.
- (UICollectionViewCell *)collectionView:(UICollectionView *)cv cellForItemAtIndexPath:(NSIndexPath *)indexPath {
/*---------
----Other CollectiveView stuffs------
-----------------*/
if([[NSFileManager defaultManager] fileExistsAtPath:YOUR_FILE_PATH isDirectory:NO])
{
imagView.image=[UIImage imageWithContentsOfFile:YOUR_FILE_PATH];
}
else
{
UIActivityIndicatorView *act=[[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
[imagView addSubview:act];
act.center=CGPointMake(imagView.frame.size.width/2, imagView.frame.size.height/2);
[act startAnimating];
__weak typeof(UIImageView) *weakImgView = imagView;
[imagView setImageWithURL:[NSURL URLWithString:REMOTE_FILE_PATH] completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType){
for(UIView *dd in weakImgView.subviews)
{
if([dd isKindOfClass:[UIActivityIndicatorView class]])
{
UIActivityIndicatorView *act=(UIActivityIndicatorView *)dd;
[act stopAnimating];
[act removeFromSuperview];
}
}
NSString *extension=[YOUR_FILE_PATH pathExtension];
[self saveImage:image withFileName:YOUR_FILE_PATH ofType:extension];
}];
}
}

I have a similar situation when I need to know the index path of cell which is going to be displayed. Ended with - (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath. Assumably, one is "didEndDisplaying", another is "willDisplayed".

Related

Collectionview doesn't show anything during runtime

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return getName.count;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
static NSString *identifier = #"Cell";
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
UIImageView *recipeImageView = (UIImageView *)[cell viewWithTag:100];
recipeImageView.image = [UIImage imageNamed:[getName objectAtIndex:indexPath.row]];
return cell;
}
When I run the app then it doesn't show anything.what are the reasons behinds that
Mostly its tag issue cause as per your crash log
[UICollectionViewCell setImage:] means image is directly set to UICollectionViewCell not recipeImageView. so you have to check proper tag value..
so ones check tag value of image view..
otherwise check that you have register your collection view cell.

UICollectionView repeats cell

I have a UICollectionView where I display a grid of images downloaded from the Internet. I am using SDWebImage to load the images. The problem I am facing is that, when I scroll through the UICollectionView, sometimes the cells repeat themselves, displaying the same image. But when the cell is scroll out of view and then brought back, it has the right image set.
-(UICollectionViewCell*) collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
NSString *CellIdentifier = #"Gallery_Cell";
GalleryCell *cell;
if (cell==nil) {
cell= (GalleryCell *)[self.flowCollection dequeueReusableCellWithReuseIdentifier:CellIdentifier forIndexPath:indexPath];
ImageItem *dets = [self.imageList objectAtIndex:indexPath.row];
NSURL *mainImageURL = [NSURL URLWithString:dets.smallImageURL];
cell.image.contentMode = UIViewContentModeScaleAspectFill;
cell.image.clipsToBounds = YES;
[cell.image setImageWithURL:mainImageURL placeholderImage:nil];
}
return cell;
}
Has this happened to anyone else? Would highly appreciate any pointers.
On GalleryCell.m you need to add prepareForReuse method and there nullify the _image variable (assuming that you have a image #property in the cell):
- (void)prepareForReuse {
[super prepareForReuse];
[self setHighlighted:NO];
_image = nil;
}
The following is stated in the "UICollectionView Class Reference":"
If you registered a class for the specified identifier and a new cell must be created, this method initializes the cell by calling its initWithFrame: method. For nib-based cells, this method loads the cell object from the provided nib file. If an existing cell was available for reuse, this method calls the cell’s prepareForReuse method instead."
Do you have a prepareForReuse method for your GalleryCell cell class?
you cell class should be a subclass of UICollectionReusableView Class. And check it.
I ended up using “didEndDisplayingCell” method of UICollectionView and ending the current Image download and setting the image to nil. This worked perfectly and there was no more “shuffling” or repetition of images! :)
use this method this vl definitely work 100%.
- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath;
{
GalleryCell *cell1 = (GalleryCell*)[_resumeCollectionView cellForItemAtIndexPath:indexPath];
cell1= nil;
}
-(UICollectionViewCell*) collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
NSString *CellIdentifier = #"Gallery_Cell";
GalleryCell *cell;
if (cell==nil) {
cell= (GalleryCell *)[self.flowCollection dequeueReusableCellWithReuseIdentifier:CellIdentifier forIndexPath:indexPath];
ImageItem *dets = [self.imageList objectAtIndex:indexPath.row];
NSURL *mainImageURL = [NSURL URLWithString:dets.smallImageURL];
[cell.image setImage:[UIImage new]];
cell.image.contentMode = UIViewContentModeScaleAspectFill;
cell.image.clipsToBounds = YES;
[cell.image setImageWithURL:mainImageURL placeholderImage:nil];
}
return cell;
}
Since the cells are being reused, you must set their contents unconditionally in cellForIndexPath:. Your code as it is sets the images eventually, but it leaves the chance for the images to be temporarily left as old values -- values set before they were reused.
A quick solution is (probably) to provide that setImageWithURL call with a placeholder image. I don't know the SDImage library, but it's a good guess that it immediately sets an imageView's image to the placeholder image if one is provided.
If you don't have or don't want a placeholder image, you can implement prepareForReuse in your UICollectionViewCell subclass and clear the imageView there.

Custom Header in UICollectionView with Interface Builder without Storyboard

I'm trying to add a custom view to the header section of my UICollectionView. I have the xib file interface builder but I don't use storyboard. I have checked the Section Header in Interface Builder but doesn't appear any UICollectionReusableView, what can I do?
The easiest solution is to do is programatically (and keep your HeaderReusableView in another XIB file):
[self.collectionView registerNib:[UINib nibWithNibName:#"ItemHeaderView" bundle:nil]
forSupplementaryViewOfKind:UICollectionElementKindSectionHeader
withReuseIdentifier:kItemSectionHeaderViewID];
Then:
- (CGSize) collectionView:(UICollectionView *)collectionView
layout:(UICollectionViewLayout *)collectionViewLayout
referenceSizeForHeaderInSection:(NSInteger)section {
return CGSizeMake(60.0f, 30.0f);// width is ignored
}
And of course:
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView
viewForSupplementaryElementOfKind:(NSString *)kind
atIndexPath:(NSIndexPath *)indexPath {
NSString *productId = nil; ///
ItemHeaderView *view = nil;
view = [collectionView dequeueReusableSupplementaryViewOfKind:kind
withReuseIdentifier:kItemSectionHeaderViewID
forIndexPath:indexPath];
view.titleLabel.text = productId;
view.backgroundColor = [UIColor yellowColor];
return view;
}

Cannot Reload Tableview from custom cell

I have a UITableView with custom tableview cells. One of my Cell has a UITextField, I'm handling the textfield delegate methods in the custom tableviewCell class. I need to reload the tableview once the user entered a text, I had done the below thing but not worked , Any idea please help me.
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField
{
UITableView *parentTable = (UITableView *)self.superview;
[parentTable reloadData];
}
Register the view controller to receive notifications that the data has been changed, and have it refresh the table when it receives one. Then have the parser send it out.
Registering for it is easy:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(reloadTableView:)
name:#"reloadTableView"
object:nil];
Your refresh method needs to be set up to receive these notifications, along these lines:
- (void)reloadTableView:(NSNotification *)notif {
[self.yourTableName reloadData];
}
And it's important to stop observing in your ViewDidUnload:
[[NSNotificationCenter defaultCenter] removeObserver:self];
Then in the parser you need to simply add this when it's complete:
[[NSNotificationCenter defaultCenter] postNotificationName:#"reloadTableView"
object:nil];
The view controller (and anyone else observing the notification with that name) will get the message and perform its task.
Thanks..
CustomCell.h
#property (nonatomic, copy) void(^tapHandler)(NSUInteger tag);
CustomCell.m
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField
{
NSUInteger tag = 10; //Need to change the tag
self.tapHandler(10);
}
Controller.m
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath
{
// Whatever content was previously there. Add the below line in addition
cell.tapHandler = ^(NSUInteger tag){
[tableView reloadData];
};
return cell
}
Hope it helps!
first , the superview of the textfiled is not UITableview, it is tableviewcell or tableviewcell.contentview (depends on your code)
then, you just need to set the tableview as your view controller's member ,or property,
then
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField
{
[self.tableview reloadData];
}
Try this,
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField
{
[self.parentTable reloadData];
}
It's better to keep a reference of table view in CustomCell
// interface
UITableView *tableView;
// propery
#property (nonatomic)UITableView * tableView;
Then
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//your stuff
cell.tableView= tableView;
}
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField
{
UITableView *parentTable = (UITableView *)textField.superview.superview;
[parentTable reloadData];
}
Don't reload whole table view for that small operation. Just reload the Cell in which the text field is. Also didnot forget to update your data source as table view cell fetch the data from the data source while the cell is loading.
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField
{
[tableViewDataSource addObject:textField.text];//this will fill the data source with data of textfield
CustomCell *cell = (CustomCell *)textField.superview.superview;
NSIndexPath *indexPath = [parentTable indexPathForCell:cell];
[parentTable reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}
By (UITableView *)self.superview; you will not get the table view. Try below code :
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField
{
YourCell *cell = (YourCell *)[[textField superview] superview]; // you will get cell
UITableView *table = cell.superview; // you will get table view
[table reloadData];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
tableReload++;
NSString *CellIdentifier = [NSString stringWithFormat:#"cell %d%d",indexPath.row,tableReload];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
cell.textLabel.text = #"Hello";
}
// Configure the cell.
return cell;
}
Try this,
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField
{
[self.yourTableName reloadData];
[self.yourTableName reloadInputViews];
}

multiple collectionviews on same screen in ios

I am using PSUICollectionView in my app in which gallery with images thumbs are loaded with horizontal scrollview.
Now I need two more collectionviews(galleries) to show two other types of pictures.
Can anyone please help me out ??
Thanks in advance.
I got it working by adding some lines of code in delegate and data source methods as follows -
// setting tag
[self.imageCollection setTag:1];
[self.finishImageCollection setTag:2];
[self.prevImageCollection setTag:3];
pragma mark -
pragma mark Collection View Data Source
- (NSString *)formatIndexPath:(NSIndexPath *)indexPath {
return [NSString stringWithFormat:#"%ld", (long)indexPath.row+1];
}
// Populating cells
- (PSUICollectionViewCell *)collectionView:(PSUICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
ImageGridCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
if(collectionView.tag==1){
cell.statusImage.image= [UIImage imageNamed:#"checked.png"];
cell.imageThumb.image = [self.startImages objectAtIndex:indexPath.row];
}
if(collectionView.tag==2){
cell.statusImage.image= [UIImage imageNamed:#"checked.png"];
cell.imageThumb.image = [self.finishImages objectAtIndex:indexPath.row];
}
if(collectionView.tag==3){
cell.statusImage.image= [UIImage imageNamed:#"checked.png"];
cell.imageThumb.image = [self.prevImages objectAtIndex:indexPath.row];
}
return cell;
}
- (NSInteger)collectionView:(PSUICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
int imgCount;
if(collectionView.tag==1)
{
[self loadStartPictures];
imgCount=self.startImages.count;
}
if(collectionView.tag==2)
{
[self loadFinishPictures];
imgCount=[self.finishImages count];
}
if(collectionView.tag==3)
{
[self loadSupervisorPictures];
imgCount=[self.prevImages count];
}
return imgCount;
}
#pragma mark -
#pragma mark Collection View Delegate
- (void)collectionView:(PSTCollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
self.allImages=[[NSMutableArray alloc]init];
UIImage *imageAtIndexPath;
NSLog(#"Delegate cell %# : SELECTED", [self formatIndexPath:indexPath]);
ImageGridCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
cell.label.backgroundColor=[UIColor underPageBackgroundColor];
if(collectionView.tag==1){
imageAtIndexPath=[self.startImages objectAtIndex:indexPath.row];
}
if(collectionView.tag==2){
imageAtIndexPath=[self.finishImages objectAtIndex:indexPath.row];
}
if(collectionView.tag==3){
imageAtIndexPath=[self.supervisorImages objectAtIndex:indexPath.row];
}
}
Simple. Just add the views like you would any other UIView's. Set their frames correctly, they should work.
Keep an outlet to each of them, then you can check to see which one of them is calling a delegate / data source method.