3

如何在 tvOS 上为我的集合视图手动决定下一个重点索引路径?

我的用例是我有一个集合视图,其中包含向各个方向滚动的动态单元格,有时当一个部分中的一个单元格跨越下一个部分中的多个单元格时,我希望能够手动决定接下来关注哪个单元格。

默认行为似乎是集合视图将选择前一个中间的单元格。

例如,绿色单元格当前处于焦点位置。当我向下导航时,集合视图想要关注红色单元格,但我希望接下来关注蓝色单元格。

在此处输入图像描述

我最初的想法是实现collectionView(_:shouldUpdateFocusIn:)委托功能,并false在集合视图选择“错误”索引路径时返回+触发焦点更新。

但是,由于某种原因,shouldUpdateFocusIn当我从它返回时会被调用多次false并导致明显的滞后。

func collectionView(_ collectionView: UICollectionView, shouldUpdateFocusIn context: UICollectionViewFocusUpdateContext) -> Bool {
    if let nextIndexPath = shouldFocusCell(context.focusHeading, (context.previouslyFocusedIndexPath, context.nextFocusedIndexPath)) {
        // The collection view did not select the index path of the cell we want to focus next.
        // Save the correct index path and trigger a focus update.
        lastFocusChange = (lastFocusChange.next, nextIndexPath)
        setNeedsFocusUpdate()
        // Using updateFocusIfNeeded() results in the following warning:
        // WARNING: Calling updateFocusIfNeeded while a focus update is in progress. This call will be ignored.
        return false
    }
    return true
}

下一个想法是在 中做同样的事情collectionView(_:didUpdateFocusIn:with:),但在这种情况下,我们只在焦点已经移动到“错误”单元格之后才更新焦点,因此用户可以清楚地看到焦点从错误单元格移动到正确单元格.

也不理想。

我正在使用我自己的 and 子类UICollectionViewLayoutUICollectionView但是我没有看到任何我可以覆盖的东西,以便能够手动决定在shouldUpdateFocusIn调用之前向上/向下/向左/向右导航时接下来要关注的索引路径。

有什么办法可以做到这一点?

4

1 回答 1

0

一种可能性是collectionView(_ collectionView:, canFocusItemAt:)让您的 collectionView 知道给定的 indexPath 是否可以接收焦点。

您可以在下面找到此概念的简单实现。您将需要根据自己的需要调整其数学。

func collectionView(_ collectionView: UICollectionView, canFocusItemAt indexPath: IndexPath) -> Bool {
    guard let currentlyFocusedCellLayoutAttributes = collectionView.layoutAttributesForItem(at: focusedIndexPath) else { return false }
    guard let cellAtGivenIndexPathLayoutAttributes = collectionView.layoutAttributesForItem(at: indexPath) else { return false }
        
    let currentlyFocusedCellOriginX = currentlyFocusedCellLayoutAttributes.frame.origin.x
    let currentlyFocusedCellOriginY = currentlyFocusedCellLayoutAttributes.frame.origin.y
    let currentlyFocusedCellWidth = currentlyFocusedCellLayoutAttributes.frame.width
        
    let cellAtGivenIndexPathOriginX = cellAtGivenIndexPathLayoutAttributes.frame.origin.x
    let cellAtGivenIndexPathOriginY = cellAtGivenIndexPathLayoutAttributes.frame.origin.y
    let cellAtGivenIndexPathWidth = cellAtGivenIndexPathLayoutAttributes.frame.width
            
    let offsetX = collectionView.contentOffset.x

    // Scrolling horizontally is always allowed
    if currentlyFocusedCellOriginY == cellAtGivenIndexPathOriginY {
        return true
    }
    
    // Scrolling vertically is only allowed to the first cell (blue cell in the screenshot)
    if cellAtGivenIndexPathOriginX <= offsetX {
        return true
    }
    
    return false
}
于 2020-12-04T15:57:47.573 回答