By Joe Jupin


2013-02-06 19:28:45 8 Comments

This isn't so much a question as an explanation of how to solve this problem.

The first thing to realize is that the UICollectionView does inherit from a UIScrollView - so doing a standard lookup with a scroll view's content is the best solution.

Here's the problem I was addressing:

I had a UICollectionView that had differing items in each cell - along with differing types of cells. I need the selection of a cell to cause an effect of the image in the cell to appear to expand and take over the whole screen. How I did the expansion is for another post.

The challenge was getting the cell's position on the screen so that the animating section would have a reference point from where to start.

So, to facilitate getting this information - consider the following code:

First note:

UICollectionView *picturesCollectionView;
DrawingCell cell; // -> instanceof UICollectionViewCell with custom items.

// first, get the list of cells that are visible on the screen - you must do this every time
// since the items can change...  This is a CRITICAL fact.  You do not go through the
// entire list of cells - only those the collectionView indicates are visible.  Note 
// there are some things to watch out for - the visibles array does not match the indexPath.item
// number - they are independent.  The latter is the item number overall the cells, while
// the visibles array may have only 2 entries - so there is NOT a 1-to-1 mapping - keep
// that in mind.

NSArray *visibles = [self.picturesCollectionView visibleCells];

// now, cycle through the times and find the one that matches some criteria.  In my
// case, check that the cell for the indexPath passed matches the cell's imageView...
// The indexPath was passed in for the method call - note that the indexPath will point
// to the number in your datasource for the particular item - this is crucial.

for (int i=0; i<visibles.count; i++) {
    DrawingCell *cell = (DrawingCell *)visibles[i];
    if (cell.imageView.image == (UIImage *)images[indexPath.item]) {

        // at this point, we've found the correct cell - now do the translation to determine
        // what is it's location on the current screen...  You do this by getting the contentOffset
        // from the collectionView subtracted from the cell's origin - and adding in (in my case)
        // the frame offset for the position of the item I wish to animate (in my case the
        // imageView contained within my custom collection cell...

        CGFloat relativeX = cell.frame.origin.x - self.picturesCollectionView.contentOffset.x + cell.imageView.frame.origin.x;
        CGFloat relativeY = cell.frame.origin.y - self.picturesCollectionView.contentOffset.y + cell.imageView.frame.origin.y;

        // I now have the exact screen coordinates of the imageView - so since I need this
        // to perform animations, I save it off in a CGRect - in my case, I set the size
        // exactly to the size of the imageView - so say you were doing a Flicker display
        // where you wanted to grow a selected image, you get the coordinates of the image
        // in the cell and the size from the displayed image...

        UIImageView *image = cell.imageView;

        // selectedCell is a CGRect that's global for the sake of this code...

        selectedCell = cell.frame;
        selectedCell.origin.x = relativeX;
        selectedCell.origin.y = relativeY;
        selectedCell.size.width = cell.imageView.frame.size.width;
        selectedCell.size.height = cell.imageView.frame.size.height;
    }
}

// done.  I have my coordinates and the size of the imageView I wish to animate and grow...

Hopefully, this helps other folks that are trying to figure out how to say overlay something on the cell in an exact position, etc...

5 comments

@Farhan Patel 2013-02-15 23:19:50

I've done this before as well. it took a while but it is possible.

You need to use

[currentImageView.superview convertRect:currentImageView.frame toView:translateView]

Where currentImageView is the image that the user taps. It's superview will be the cell. You want to convert the rect of your image to where it actually is on a different view. That view is called "translateView" here.

So what is translateView? In most cases it is just self.view.

This will give you a new frame for your imageview that will meet where your image is on your table. Once you have that you can expand the image to take up the entire screen.

Here is a gist of the code I use to tap an image then expand the image and display a new controller that allows panning of the image.

https://gist.github.com/farhanpatel/4964372

@OhadM 2015-12-24 09:39:49

@Alivin solution using layoutAttributesForItemAtIndexPath works but only for the presented/current scroll view that the user sees.

Meaning, if you select the first presented visible cells you will get the actual frame, but if you scroll, the frame will have a deviation and you won't get the coordinates you need.

This is why you need to use convertPoint:toView :

let realCenter = collectionView.convertPoint(cell.center, toView: collectionView.superview)

Basically this method takes a point (cell.center) in one view and convert that point to another view (collectionView.superview) coordinate system which is exactly what we need.

Thus, realCenter will always contain the coordinates to the actual selected cell.

@Roi Mulia 2016-11-24 12:47:42

You sir, should win a reward.

@Jack Daniel 2018-11-04 19:19:32

what I actually looking for

@Amit Verma 2019-09-05 21:38:50

This is what i was looking for from last 5 hours

@Alivin 2013-09-07 13:33:12

-(void)collectionView:(UICollectionView *)cv didSelectItemAtIndexPath:(NSIndexPath *)indexPath;
{

UICollectionViewLayoutAttributes *attributes = [cv layoutAttributesForItemAtIndexPath:indexPath];

CGRect cellRect = attributes.frame;

CGRect cellFrameInSuperview = [cv convertRect:cellRect toView:[cv superview]];

NSLog(@"%f",cellFrameInSuperview.origin.x);
}

It work for me.You can try yourself

@Meet 2014-09-28 12:15:49

Very much perfect! Just what I was looking for.

@Ben 2014-10-13 00:18:16

Perfect! So to recap, cellRect is basically the position of the cell in the collectionView Content, it's not usually going to change. CellFrameInSuperview is the absolute position of the cell related to the superview. Check cellFrameInSuperView against CellRect and you'll be able to check if the cell is visible in it's entirety.

@Shaik Riyaz 2015-05-11 09:20:26

perfect answer :)

@mdonati 2015-09-21 17:16:23

Amazing :) Thanks!

@ejanowski 2015-10-28 16:48:02

Work like a charm

@Rifinio 2015-11-25 10:29:19

Worked for me, thank you.

@Coder ACJHP 2019-04-08 12:52:14

👏🏻 👏🏻 👏🏻 👏🏻 this is the correct answer, always giving correct frame

@mdb983 2013-07-03 02:54:51

An alternative is to use the events to provide most if not all the answers to your questions.

I presume that a touch event will initiate all of this, so lets implement something meaningful;

  //First, trap the event in the collectionView controller with;

- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{

   // lets ensure it's actually visible (yes we got here from a touch event so it must be... just more info)
   if ([self.collectionView.indexPathsForVisibleItems containsObject:indexPath]) {

     // get a ref to the UICollectionViewCell at indexPath
     UICollectionViewCell *cell =(UICollectionViewCell *)[self.collectionView cellForItemAtIndexPath:indexPath];

     //finally get the rect for the cell
     CGRect cellRect = cell.frame


      // do your processing here... a ref to the cell will allow you to drill down to the image without the headache!!


  }
}

oh ... before you rush off for happy hour, lets not forget to read up on;

   <UICollectionViewDelegate> (hint - it's needed)  

@elio.d 2013-02-06 19:42:14

Well the first part of your question is pretty much clear, the second one?? anyway if what you want to get is the frame of the select cell in your collection you can use this :

UICollectionViewLayoutAttributes *attributes = [self.collectionView layoutAttributesForItemAtIndexPath:indexPath];
CGRect cellRect = attributes.frame;

More info here

@Joe Jupin 2013-02-07 03:11:36

Actually a lot of code was cut off - or else you would have seen that you can't do that - you have to first ensure the item is visible - otherwise you get a frame entirely off the screen.

@Joe Jupin 2013-02-07 03:12:44

I keep hitting the wrong buttons - anyway, once you go through the visibles array - and find the one you're looking at, then you can merely grab it's frame like this:

@Joe Jupin 2013-02-07 03:14:38

Dang it - hit the return again: here's the gist of it - get the cell from the visibles - and then, merely grab it's cell.frame - no need to go through the attributes method you showed above. I need to remove this post - and do the whole thing proper again - the whole thing was pretty mangled and did not realize it until I tried to scroll up and down in the code section...

@Calin Chitu 2014-10-16 09:30:06

Nice code for iOS8, though on iOS7 I had to use [view convertRect:... fromView:...]

Related Questions

Sponsored Content

26 Answered Questions

1 Answered Questions

[SOLVED] Swift vertical UICollectionView inside UITableView

0 Answered Questions

UIImageViews with Collection cell not sizing properly on viewDidLoad()

0 Answered Questions

UICollectionView screen-sized cell rotation?

5 Answered Questions

1 Answered Questions

1 Answered Questions

Sponsored Content