简体   繁体   English

从 UICollectionView 获取单元格的屏幕位置

[英]Getting the screen location of a cell from a UICollectionView

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.要意识到的第一件事是UICollectionView确实从UIScrollView继承 - 因此使用滚动视图的内容进行标准查找是最好的解决方案。

Here's the problem I was addressing:这是我要解决的问题:

I had a UICollectionView that had differing items in each cell - along with differing types of cells.我有一个UICollectionView ,每个单元格中都有不同的项目 - 以及不同类型的单元格。 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...希望这可以帮助其他人试图弄清楚如何在单元格上的确切位置叠加某些东西,等等......

-(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它对我有用。你可以自己试试

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更多信息在这里

@Alivin solution using layoutAttributesForItemAtIndexPath works but only for the presented/current scroll view that the user sees.使用layoutAttributesForItemAtIndexPath @Alvin 解决方案有效,但仅适用于用户看到的呈现/当前滚动视图。

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.意思是,如果您选择第一个显示的可见单元格,您将获得实际的frame如果您滚动,则该frame会有偏差,您将无法获得所需的坐标。

This is why you need to use convertPoint:toView :这就是您需要使用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.基本上这个方法在一个视图中取一个点(cell.center)并将该点转换为另一个视图(collectionView.superview)坐标系,这正是我们所需要的。

Thus, realCenter will always contain the coordinates to the actual selected cell.因此, realCenter将始终包含实际选定单元格的坐标。

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.其中 currentImageView 是用户点击的图像。 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.该视图在此处称为“translateView”。

So what is translateView?那么什么是翻译视图? In most cases it is just self.view .在大多数情况下,它只是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 https://gist.github.com/farhanpatel/4964372

I needed to know the exact location of the cell's center that a user tapped relative to the UIWindow.我需要知道用户点击的单元格中心相对于 UIWindow 的确切位置。 In my situation the collectionView was a child of a view that took up 2/3 of the screen and its superview was a child of another view.在我的情况下,collectionView 是占据屏幕 2/3 的视图的子视图,它的超级视图是另一个视图的子视图。 Long story short using the collectionView.superView wasn't suffice and I needed the window.长话短说,使用 collectionView.superView 是不够的,我需要窗口。 I used Ohadman's answer above and this answer from TomerBu to get the tapped location of the screen/window's coordinate system.我使用了上面的 Ohadman 答案TomerBu 的这个答案来获取屏幕/窗口坐标系的点击位置。

Assuming your app has 1 window that it isn't connected across multiple screens, I used both of these and the same exact location printed out.假设您的应用程序有 1 个窗口,它没有跨多个屏幕连接,我使用这两个窗口并打印出相同的确切位置。

It's important to know that this is going to give you the exact middle of the cell (relative to the window).重要的是要知道这将为您提供单元格的确切中间(相对于窗口)。 Even if you touch the top, left, bottom or right side of the cell it's going to return the coordinate of the center of the cell itself and not the exact location that you tapped.即使您触摸单元格的顶部、左侧、底部或右侧,它也会返回单元格本身中心的坐标,而不是您点击的确切位置。

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {

    guard let cell = collectionView.cellForItem(at: indexPath) as? YourCell else { return }
    guard let layoutAttributes = collectionView.layoutAttributesForItem(at: indexPath) else { return }
    guard let window = UIApplication.shared.keyWindow else { return }

    let touchedLocationInWindow = collectionView.convert(cell.center, to: window)
    print("OhadM's version: \(touchedLocationInWindow)")

    let cPoint = layoutAttributes.center
    let tappedLocationInWindow = collectionView.convert(cPoint, to: window)
    print("TomerBu's version: \(tappedLocationInWindow)")
}

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)  

SWIFTYFIED (v5) SHORT ANSWER SWIFTYFIED (v5) 简短回答

let attributes = collectionView.layoutAttributesForItem(at: indexPath)
let cellRect = attributes?.frame
let cellFrameInSuperview = collectionView.convert(cellRect ?? CGRect.zero, to: collectionView.superview)

All you need is the index path of the cell.您只需要单元格的索引路径。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM