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

时间:2013-02-06 19:28:45

标签: ios uicollectionview uicollectionviewcell

这不是解释如何解决这个问题的问题。

要实现的第一件事是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...

希望这有助于其他人试图弄清楚如何在一个确切的位置上覆盖某些东西等等......

7 个答案:

答案 0 :(得分:71)

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

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

答案 1 :(得分:40)

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

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

更多信息here

答案 2 :(得分:16)

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

意思是,如果您选择第一个显示的可见单元格,您将获得实际的frame,如果您滚动,frame将有偏差,你不会得到你需要的坐标。

这就是你需要使用 convertPoint:toView

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

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

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

答案 3 :(得分:7)

我之前也做过这个。花了一段时间但是有可能。

您需要使用

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

其中currentImageView是用户点击的图像。它的超级视图将是细胞。 您希望将图像的矩形转换为实际位于不同视图的位置。该视图在此处称为“translateView”。

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

这将为您的imageview提供一个新的框架,以满足您的图像在桌面上的位置。完成后,您可以展开图像以占据整个屏幕。

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

<击> https://gist.github.com/farhanpatel/4964372

答案 4 :(得分: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)  

答案 5 :(得分:1)

我需要知道用户相对于UIWindow点击的单元格中心的确切位置。在我的情况下,collectionView是一个视图的子视图,该视图占据了屏幕的2/3,而其superview是另一个视图的子视图。长话短说,使用collectionView.superView是不够的,我需要一个窗口。我用Ohadman's answer above和这个answer from TomerBu来获取屏幕/窗口坐标系的点击位置。

假设您的应用有1个没有跨多个屏幕连接的窗口,则我使用了这两个窗口,并打印了相同的确切位置。

重要的是要知道,这将为您提供单元格的确切中间位置(相对于窗口)。即使您触摸单元格的顶部,左侧,底部或右侧,它也会返回单元格中心的坐标,而不是您所点击的确切位置。

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)")
}

答案 6 :(得分:-1)

SWIFTYFIED (v5) 简短回答

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

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