55

这与其说是一个问题,不如说是对如何解决这个问题的解释。

首先要意识到的是UICollectionView确实继承自UIScrollView- 所以使用滚动视图的内容进行标准查找是最好的解决方案。

这是我要解决的问题:

我有一个UICollectionView在每个单元格中有不同项目的单元格 - 以及不同类型的单元格。我需要选择一个单元格以使单元格中的图像看起来扩大并占据整个屏幕。我如何进行扩展是另一篇文章。

挑战在于获得单元格在屏幕上的位置,以便动画部分有一个从哪里开始的参考点。

因此,为了便于获取此信息 - 请考虑以下代码:

第一个注意事项:

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...

希望这可以帮助其他试图弄清楚如何在单元格上以精确位置覆盖某些东西的人,等等......

4

7 回答 7

76
-(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);
}

它对我有用。你可以自己试试

于 2013-09-07T13:33:12.613 回答
41

好吧,您问题的第一部分非常清楚,第二部分?无论如何,如果您想要获得的是集合中选择单元格的框架,您可以使用它:

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

更多信息在这里

于 2013-02-06T19:42:14.310 回答
20

@Alivin 解决方案使用layoutAttributesForItemAtIndexPath有效,但仅适用于用户看到的呈现/当前滚动视图。

这意味着,如果您选择第一个显示的可见单元格,您将获得实际的frame如果您滚动,则frame会有偏差,您将无法获得所需的坐标。

这就是您需要使用convertPoint:toView的原因:

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

基本上,这种方法在一个视图中获取一个点(cell.center)并将该点转换为另一个视图(collectionView.superview)坐标系,这正是我们所需要的。

因此,realCenter将始终包含实际选定单元格的坐标。

于 2015-12-24T09:39:49.380 回答
9

我以前也这样做过。花了一段时间,但这是可能的。

你需要使用

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

currentImageView 是用户点击的图像。它的超级视图将是单元格。您想将图像的矩形转换为它在不同视图上的实际位置。该视图在这里称为“translateView”。

那么什么是 translateView?在大多数情况下,它只是self.view.

这将为您的图像视图提供一个新框架,该框架将满足您的图像在您的桌子上的位置。一旦你有了它,你可以扩展图像以占据整个屏幕。

这是我用来点击图像然后展开图像并显示允许平移图像的新控制器的代码要点。

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

于 2013-02-15T23:19:50.767 回答
2

I needed to know the exact location of the cell's center that a user tapped relative to the 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. Long story short using the collectionView.superView wasn't suffice and I needed the window. I used Ohadman's answer above and this answer from TomerBu to get the tapped location of the screen/window's coordinate system.

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.

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)")
}
于 2020-05-18T00:06:41.917 回答
1

另一种方法是使用这些事件来为您的问题提供大部分(如果不是全部)答案。

我假设一个触摸事件会启动所有这些,所以让我们实现一些有意义的东西;

  //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!!


  }
}

哦……在你赶去欢乐时光之前,别忘了继续阅读;

   <UICollectionViewDelegate> (hint - it's needed)  
于 2013-07-03T02:54:51.463 回答
1

快速 (v5) 简短答案

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

您所需要的只是单元格的索引路径。

于 2021-02-09T09:01:24.210 回答