Поведение движка пользовательского фокуса для UICollectionView

Я использую стандартный UICollectionView с разделами. Мои ячейки выложены как сетка. Следующая ячейка в выбранном направлении фокусируется правильно, если пользователь перемещает фокус с помощью пульта Apple TV. Но если в сетке есть «пробел», механизм фокусировки по умолчанию перескакивает через разделы. Это делается для того, чтобы сфокусировать ячейку, которая может находиться в нескольких разделах, но находится в том же столбце.

Простой пример: есть 3 раздела. В первой секции 3 ячейки. Второй имеет 2 ячейки, а последний снова имеет 3 ячейки. См. следующее изображение:

введите описание изображения здесь

Если зеленая ячейка сфокусирована и пользователь касается направления вниз, желтая ячейка становится сфокусированной, а второй раздел пропускается механизмом фокусировки.

Я хотел бы заставить его, чтобы никакие разделы не могли быть перепрыгнуты. Поэтому вместо того, чтобы фокусировать желтую ячейку, я хотел бы сфокусировать синюю ячейку.

Я узнал, что механизм Apple TV Focus внутренне работает как система сетки и что описанное поведение является поведением по умолчанию. Чтобы разрешить другие движения (например, по диагонали), нам нужно помочь механизму фокусировки, поместив невидимые UIFocusGuide, которые могут перенаправить механизм фокусировки на preferredFocusedView.

Итак, на следующем изображении есть одна невидимая красная направляющая фокуса, помещенная в пустое пространство секции UICollectionView, которая перенаправляет фокус вниз на нужную синюю ячейку. Я думаю, что это было бы идеальным решением, в теории.

введите описание изображения здесь

Но как мне добавить UIFocusGuides ко всем пустым местам UICollectionView разделов? Я пробовал несколько вещей, но ничего не получилось. Возможно, добавьте его как представление декоратора, но это кажется неправильным. Или как дополнительные ячейки, но это ломает слой данных и не работают привязки ограничений.

Кто-нибудь знает, как добавить UIFocusGuides к UICollectionView?


person Stefan Arn    schedule 06.12.2015    source источник
comment
Та же проблема для меня. Вы пытались переопределить collectionView:canFocusItemAtIndexPath и вернуть false в желтую ячейку?   -  person El Belga    schedule 15.01.2016
comment
Нет у меня нет. Но я предполагаю, что это может создать небольшой беспорядок, потому что ячейки должны быть фокусируемыми. Я по-прежнему предпочитаю технику UIFocusGuide. Но до сих пор не знаю, как этого добиться. Я также спрашивал на форумах Apple: forums.developer.apple.com/message/93020 Пожалуйста, сообщите мне, если вы нашли решение.   -  person Stefan Arn    schedule 15.01.2016


Ответы (3)


Два пути к достижению цели. По крайней мере то, что я пробовал и у меня получилось. знак равно Могут быть и другие способы.

1:

Самый простой способ достичь своей цели — добавить вертикальное представление коллекции с ячейками, содержащими горизонтальное представление коллекции. Каждая горизонтальная коллекция — это ваш раздел.

Убедитесь, что вы добавили следующий код в представление вертикальной коллекции:

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

2:

Если вы хотите использовать UIFocusGuide, я думаю, что хорошим местом для добавления руководства по фокусировке является заголовок раздела. Убедитесь, что ваше руководство по фокусировке находится в заголовке раздела, и обновите файл selectedFocusedView для переключения на каждый раздел. В моем случае я назначаю первую ячейку каждого раздела, когда покидаю раздел.

person HMHero    schedule 27.05.2016

Одним из решений является создание подкласса 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 того же размера, что и сам дополнительный вид. Вот и все.

Полную реализацию описанного метода можно найти здесь на gitHub

person Stefan Arn    schedule 12.08.2016

Возможно, вы могли бы попытаться реализовать метод optional func indexPathForPreferredFocusedView(in collectionView: UICollectionView) -> IndexPath?, который является частью UICollectionViewDelegate ?

Реализует этот метод для вашего Раздела 1, 2, 3... UICollectionView и возвращает, скажем, IndexPath(item: 0, section: 0), если вы хотите, чтобы механизм фокусировки автоматически фокусировался на первом элементе.

Обратите внимание, что если вы хотите также установить sectionCollectionView.remembersLastFocusedIndexPath= true

person frouo    schedule 20.08.2019