UICollectionView的自定义焦点引擎行为

时间:2015-12-06 17:43:28

标签: uicollectionview focus tvos apple-tv

我使用标准UICollectionView和部分。我的细胞像网格一样布局。如果用户使用Apple TV遥控器移动焦点,则所选方向上的下一个单元格会正确聚焦。但是,如果有一个"差距"在网格中,默认焦点引擎跳过节。这样做的目的是聚焦一个可能是几个部分但位于同一列的单元格。

简单示例: 共有3个部分。第一部分有3个单元格。第二个有2个单元,最后一个有3个单元。见下图:

enter image description here

如果绿色单元格被聚焦并且用户触摸向下方向,则黄色单元格被聚焦并且聚焦引擎跳过第二部分。

我想强迫它没有任何部分可以跳过。因此,我不想聚焦黄色细胞而是聚焦蓝色细胞。

我了解到Apple TV Focus引擎内部的工作方式类似于网格系统,所描述的行为是默认行为。为了允许其他移动(例如对角线),我们需要通过放置可以将焦点引擎重定向到UIFocusGuide的不可见preferredFocusedView来帮助焦点引擎。

因此,在下图中,有一个不可见的红色焦点指南放置在UICollectionView部分的空白区域中,该指南会将向下焦点重定向到所需的蓝色单元格。我认为这将是理论上的完美解决方案。

enter image description here

但是如何将UIFocusGuide添加到UICollectionView个部分的所有空格?我尝试了几件事,但没有任何效果。也许将它添加为装饰器视图,但这似乎是错误的。或者作为附加单元格,但是会破坏数据层并且约束锚点不起作用。

有没有人知道如何将UIFocusGuide添加到UICollectionView

3 个答案:

答案 0 :(得分:0)

实现目标的两种方法。至少我尝试过并让它发挥作用。 =)。可能还有其他方法。

1:

实现目标的最简单方法是使用包含水平集合视图的单元格添加垂直集合视图。每个水平集合都是您的部分。

确保将以下代码添加到垂直集合视图中:

func collectionView(collectionView: UICollectionView, canFocusItemAtIndexPath indexPath: NSIndexPath) -> Bool {
    return false
}

2:

如果您想使用UIFocusGuide,我认为添加焦点指南的好地方是节标题。确保您的焦点指南在节标题中并更新preferredFocusedView作为切换到每个部分。在我的情况下,当我离开该部分时,我会分配每个部分的第一个单元格。

答案 1 :(得分:0)

一个解决方案是将UICollectionViewFlowLayout子类化为一个布局,在所有空白区域的顶部添加UIFocusGuide的补充视图。

基本上,自定义流布局在prepareLayout中计算所需的布局属性,如下所示:

self.supplementaryViewAttributeList = [NSMutableArray array];
if(self.collectionView != nil) {
    // calculate layout values
    CGFloat contentWidth = self.collectionViewContentSize.width - self.sectionInset.left - self.sectionInset.right;
    CGFloat cellSizeWithSpacing = self.itemSize.width + self.minimumInteritemSpacing;
    NSInteger numberOfItemsPerLine = floor(contentWidth / cellSizeWithSpacing);
    CGFloat realInterItemSpacing = (contentWidth - (numberOfItemsPerLine * self.itemSize.width)) / (numberOfItemsPerLine - 1);

    // add supplementary attributes
    for (NSInteger numberOfSection = 0; numberOfSection < self.collectionView.numberOfSections; numberOfSection++) {
        NSInteger numberOfItems = [self.collectionView numberOfItemsInSection:numberOfSection];
        NSInteger numberOfSupplementaryViews = numberOfItemsPerLine - (numberOfItems % numberOfItemsPerLine);

        if (numberOfSupplementaryViews > 0 && numberOfSupplementaryViews < 6) {
            NSIndexPath *indexPathOfLastCellOfSection = [NSIndexPath indexPathForItem:(numberOfItems - 1) inSection:numberOfSection];
            UICollectionViewLayoutAttributes *layoutAttributesOfLastCellOfSection = [self layoutAttributesForItemAtIndexPath:indexPathOfLastCellOfSection];

            for (NSInteger numberOfSupplementor = 0; numberOfSupplementor < numberOfSupplementaryViews; numberOfSupplementor++) {
                NSIndexPath *indexPathOfSupplementor = [NSIndexPath indexPathForItem:(numberOfItems + numberOfSupplementor) inSection:numberOfSection];
                UICollectionViewLayoutAttributes *supplementaryLayoutAttributes = [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:ARNCollectionElementKindFocusGuide withIndexPath:indexPathOfSupplementor];
                supplementaryLayoutAttributes.frame = CGRectMake(layoutAttributesOfLastCellOfSection.frame.origin.x + ((numberOfSupplementor + 1) * (self.itemSize.width + realInterItemSpacing)), layoutAttributesOfLastCellOfSection.frame.origin.y, self.itemSize.width, self.itemSize.height);
                supplementaryLayoutAttributes.zIndex = -1;

                [self.supplementaryViewAttributeList addObject:supplementaryLayoutAttributes];
            }
        }
    }
}

然后在layoutAttributesForSupplementaryViewOfKind:方法中返回所需的布局,如下所示:

if ([elementKind isEqualToString:ARNCollectionElementKindFocusGuide]) {
    for (UICollectionViewLayoutAttributes *supplementaryLayoutAttributes in self.supplementaryViewAttributeList) {
        if ([indexPath isEqual:supplementaryLayoutAttributes.indexPath]) {
            layoutAttributes = supplementaryLayoutAttributes;
        }
    }
}

现在,您的补充视图只需要与补充视图本身相同的UIFocusGuide大小。就是这样。

可以找到所述方法的完整实现here on gitHub

答案 2 :(得分:0)

也许您可以尝试实现UICollectionViewDelegate的{​​{3}}方法?

为您的第1、2、3 ... UICollectionView实施此方法,并返回IndexPath(item: 0, section: 0),如果您希望焦点引擎自动对第一项进行聚焦。

请注意,如果您还要设置sectionCollectionView. optional func indexPathForPreferredFocusedView(in collectionView: UICollectionView) -> IndexPath? = true