UICollectionView的自定义焦点引擎行为

Ste*_*Arn 8 focus apple-tv uicollectionview tvos

我使用标准UICollectionView的部分.我的细胞像网格一样布局.如果用户使用Apple TV遥控器移动焦点,则所选方向上的下一个单元格会正确聚焦.但是如果网格中存在"间隙",则默认焦点引擎跳过部分.这样做是为了聚焦一个可能在几个部分之外但在同一列中的细胞.

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

在此输入图像描述

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

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

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

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

在此输入图像描述

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

有没有人知道如何添加UIFocusGuides UICollectionView

Ste*_*Arn 0

一种解决方案是UICollectionViewFlowLayout使用布局进行子类化,该布局UIFocusGuide为所有空白区域添加顶部带有 s 的补充视图。

基本上,自定义流布局计算所需的布局属性,如下所示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];
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

可以在 gitHub 上找到所描述方法的完整实现