Подтвердить что ты не робот

Выбор ячейки UICollectionView и повторное использование ячеек

При выборе ячейки я хочу обработать изменение внешнего вида ячейки. Я понял, что метод делегата collectionView:didSelectItemAtIndexPath: и collectionView:didDeselectItemAtIndexPath: - это то, где я должен редактировать ячейку.

-(void)collectionView:(UICollectionView *)collectionView 
       didSelectItemAtIndexPath:(NSIndexPath *)indexPath {

    DatasetCell *datasetCell = 
      (DatasetCell *)[collectionView cellForItemAtIndexPath:indexPath];

    [datasetCell replaceHeaderGradientWith:[UIColor skyBlueHeaderGradient]];
    datasetCell.backgroundColor = [UIColor skyBlueColor];
}

и

-(void)collectionView:(UICollectionView *)collectionView 
       didDeselectItemAtIndexPath:(NSIndexPath *)indexPath {

    DatasetCell *datasetCell = 
      (DatasetCell *)[collectionView cellForItemAtIndexPath:indexPath];

    [datasetCell replaceHeaderGradientWith:[UIColor grayGradient]];
    datasetCell.backgroundColor = [UIColor myDarkGrayColor];
}

Это работает отлично, за исключением случаев, когда ячейка повторно используется. Если я выбираю ячейку по индексу (0, 0), она меняет внешний вид, но когда я прокручиваю вниз, в выбранном состоянии есть еще одна ячейка.

Я считаю, что я должен использовать метод UICollectionViewCell -(void)prepareForReuse для подготовки ячейки для повторного использования (т.е. установить внешний вид ячейки в не выбранное состояние), но это дает мне трудности.

-(void)prepareForReuse {
    if ( self.selected ) {
        [self replaceHeaderGradientWith:[UIColor skyBlueHeaderGradient]];
        self.backgroundColor = [UIColor skyBlueColor];
    } else {
        [self replaceHeaderGradientWith:[UIColor grayGradient]];
        self.backgroundColor = [UIColor myDarkGrayColor];
    }
}

Когда я прокручиваю назад вверх, ячейка с индексом (0, 0) находится в невыбранном состоянии.

Когда я просто использовал свойство cell.backgroundView, чтобы этого не произошло, выполните следующие действия:

-(void)prepareForReuse {
    self.selected = FALSE;
}

и состояние выбора работало по назначению.

Любые идеи?

4b9b3361

Ответ 1

Ваше наблюдение верное. Такое поведение происходит из-за повторного использования клеток. Но вам не нужно ничего делать с prepareForReuse. Вместо этого сделайте свой чек в cellForItem и соответствующим образом настройте свойства. Что-то вроде..

 - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"cvCell" forIndexPath:indexPath];


if (cell.selected) {
     cell.backgroundColor = [UIColor blueColor]; // highlight selection 
}
else
{
     cell.backgroundColor = [UIColor redColor]; // Default color
}
return cell;
}

-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath  {

    UICollectionViewCell *datasetCell =[collectionView cellForItemAtIndexPath:indexPath];
    datasetCell.backgroundColor = [UIColor blueColor]; // highlight selection
 }  

 -(void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath {

UICollectionViewCell *datasetCell =[collectionView cellForItemAtIndexPath:indexPath]; 
datasetCell.backgroundColor = [UIColor redColor]; // Default color
}

Ответ 2

Framework будет обрабатывать переключение просмотров для вас после настройки вашей ячейки backgroundView и selectedBackgroundView, см. пример из Управление визуальное состояние для выбора и основных параметров:

UIView* backgroundView = [[UIView alloc] initWithFrame:self.bounds];
backgroundView.backgroundColor = [UIColor redColor];
self.backgroundView = backgroundView;

UIView* selectedBGView = [[UIView alloc] initWithFrame:self.bounds];
selectedBGView.backgroundColor = [UIColor whiteColor];
self.selectedBackgroundView = selectedBGView;

вам нужен только ваш класс, который реализует выделение и выделение ячеек UICollectionViewDelegate, таких как:

- (BOOL)collectionView:(UICollectionView *)collectionView
        shouldHighlightItemAtIndexPath:(NSIndexPath *)indexPath
{
    return YES;
}

- (BOOL)collectionView:(UICollectionView *)collectionView
        shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath;
{
    return YES;
}

Это работает.

Ответ 3

UICollectionView изменилось в iOS 10, введя некоторые проблемы для решений выше.

Вот хорошее руководство: https://littlebitesofcocoa.com/241-uicollectionview-cell-pre-fetching

Клетки теперь остаются немного после выхода за пределы экрана. Это означает, что иногда мы не можем получить ячейку в didDeselectItemAt indexPath, чтобы ее настроить. Затем он может отображаться на экране без обновления и без повторного использования. prepareForReuse не помогает этому углу.

Самое простое решение - отключить новую прокрутку, установив isPrefetchingEnabled в значение false. При этом управление отображением ячейки с помощью   cellForItemAt didSelect didDeselect работает так, как раньше.

Однако, если вы предпочитаете сохранять новое плавное поведение прокрутки, лучше использовать willDisplay:

func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
    let customCell = cell as! CustomCell
    if customCell.isSelected {
        customCell.select()
    } else {
        customCell.unselect()
    }
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CustomCell", for: indexPath) as! CustomCell
    //Don't even need to set selection-specific things here as recycled cells will also go through willDisplay
    return cell
}

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    let cell = collectionView.cellForItem(at: indexPath) as? CustomCell
    cell?.select()
}

func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
    let cell = collectionView.cellForItem(at: indexPath) as? CustomCell
    cell?.unselect() // <----- this can be null here, and the cell can still come back on screen!
}

С приведенным выше вы управляете ячейкой, когда она выбрана, не выбрана на экране, переработана и просто повторно отображена.

Ответ 4

Анил был на правильном пути (его решение похоже, что он должен работать, я разработал это решение независимо от его). Я все еще использовал метод prepareForReuse:, чтобы установить ячейку selected в FALSE, затем в тесте cellForItemAtIndexPath, чтобы проверить, находится ли индекс ячейки в `collectionView.indexPathsForSelectedItems ', если это так, выделите его.

В пользовательской ячейке:

-(void)prepareForReuse {
    self.selected = FALSE;
}

В cellForItemAtIndexPath: для обработки ячеек повторного использования подсветки и де-подсветки:

if ([collectionView.indexPathsForSelectedItems containsObject:indexPath]) {
    [collectionView selectItemAtIndexPath:indexPath animated:FALSE scrollPosition:UICollectionViewScrollPositionNone];
    // Select Cell
}
else {
    // Set cell to non-highlight
}

И затем обработайте выделение и дефокусировку ячейки в didDeselectItemAtIndexPath: и didSelectItemAtIndexPath:

Это работает как прелесть для меня.

Ответ 5

У меня было представление коллекции с горизонтальной прокруткой (я использую представление коллекции в Tableview), и я тоже сталкивался с проблемами с повторным использованием элементов, когда я выбираю один элемент и прокручиваю вправо, некоторые другие ячейки в следующем видимом наборе автоматически выбираются. Попытка решить эту проблему с помощью любых пользовательских свойств ячеек, таких как "выбранные", выделенные и т.д. Не помогла мне, поэтому я придумал решение ниже, и это сработало для меня.

Шаг 1:

Создайте переменную в коллекцииView для хранения выбранного индекса, здесь я использовал переменную уровня класса с именем selectedIndex

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath

{

    MyCVCell *cell = (MyCVCell*)[collectionView dequeueReusableCellWithReuseIdentifier:@"MyCVCell" forIndexPath:indexPath];    

// When scrolling happens, set the selection status only if the index matches the selected Index

if (selectedIndex == indexPath.row) {

        cell.layer.borderWidth = 1.0;

        cell.layer.borderColor = [[UIColor redColor] CGColor];

    }
    else
    {
        // Turn off the selection
        cell.layer.borderWidth = 0.0;

    }
    return cell;

}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath

{
    MyCVCell *cell = (MyCVCell *)[collectionView cellForItemAtIndexPath:indexPath];
    // Set the index once user taps on a cell
    selectedIndex = indexPath.row;
    // Set the selection here so that selection of cell is shown to ur user immediately
    cell.layer.borderWidth = 1.0;
    cell.layer.borderColor = [[UIColor redColor] CGColor];
    [cell setNeedsDisplay];
}

- (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath

{

    MyCVCell *cell = (MyCVCell *)[collectionView cellForItemAtIndexPath:indexPath];

    // Set the index to an invalid value so that the cells get deselected
    selectedIndex = -1;
    cell.layer.borderWidth = 0.0;
    [cell setNeedsDisplay];

}

-anoop

Ответ 6

В вашей пользовательской ячейке создайте открытый метод:

- (void)showSelection:(BOOL)selection
{
    self.contentView.backgroundColor = selection ? [UIColor blueColor] : [UIColor white];
}

Также напишите redefenition -prepareForReuse метод ячейки:

- (void)prepareForReuse
{
    [self showSelection:NO];
    [super prepareForReuse];
}

И в вашем ViewController вы должны иметь переменную _selectedIndexPath, которая определена в -didSelectItemAtIndexPath и сбрасывается в -didDeselectItemAtIndexPath

NSIndexPath *_selectedIndexPath;

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *cellIdentifier = @"Cell";
    UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];

    if (_selectedIndexPath) {
        [cell showSelection:[indexPath isEqual:_selectedIndexPath]];
    }
}

- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
    UICollectionViewCell *cell = [collectionView cellForItemAtIndexPath:indexPath];
    [cell showSelection:![indexPath isEqual:_selectedIndexPath]];// on/off selection
    _selectedIndexPath = [indexPath isEqual:_selectedIndexPath] ? nil : indexPath;
}

- (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath
{
    UICollectionViewCell *cell = [collectionView cellForItemAtIndexPath:indexPath];
    [cell showSelection:NO];
    _selectedIndexPath = nil;
}

Ответ 7

Только решение @stefanB работало для меня на iOS 9.3

Здесь я должен изменить для Swift 2

func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {

        //prepare your cell here..

        //Add background view for normal cell
        let backgroundView: UIView = UIView(frame: cell!.bounds)
        backgroundView.backgroundColor = UIColor.lightGrayColor()
        cell!.backgroundView = backgroundView

        //Add background view for selected cell
        let selectedBGView: UIView = UIView(frame: cell!.bounds)
        selectedBGView.backgroundColor = UIColor.redColor()
        cell!.selectedBackgroundView = selectedBGView

        return cell!
 }

 func collectionView(collectionView: UICollectionView, shouldHighlightItemAtIndexPath indexPath: NSIndexPath) -> Bool {
        return true
 }

 func collectionView(collectionView: UICollectionView, shouldSelectItemAtIndexPath indexPath: NSIndexPath) -> Bool {
        return true
 }

Ответ 8

вы можете просто установить selectedBackgroundView ячейки как backgroundColor = x.

Теперь, когда вы нажмете на ячейку, его выбранный режим изменится автоматически и перейдет к цвету фона, чтобы перейти на x.

Ответ 9

Благодаря вашему ответу @RDC.

Следующие коды работают с Swift 3

// MARK: - UICollectionViewDataSource protocol
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

    //prepare your cell here..
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath as IndexPath) as! MyCell
    cell.myLabel.text =  "my text"

    //Add background view for normal cell
    let backgroundView: UIView = UIView(frame: cell.bounds)
    backgroundView.backgroundColor = UIColor.lightGray
    cell.backgroundView = backgroundView

    //Add background view for selected cell
    let selectedBGView: UIView = UIView(frame: cell.bounds)
    selectedBGView.backgroundColor = UIColor.green
    cell.selectedBackgroundView = selectedBGView

    return cell
}

// MARK: - UICollectionViewDelegate protocol
func collectionView(_ collectionView: UICollectionView, shouldHighlightItemAt indexPath: IndexPath) -> Bool {
    return true
}

func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool {
    return true
}

Ответ 10

Изменение свойства ячейки, такого как цвета фона ячейки, не должно выполняться на самом UICollectionViewController, это должно быть сделано внутри класса CollectionViewCell. Не используйте didSelect и didDeselect, просто используйте это:

class MyCollectionViewCell: UICollectionViewCell 
{
     override var isSelected: Bool
     {
         didSet
         {
            // Your code
         }
     } 
}

Ответ 11

Что я сделал, чтобы решить это, нужно внести изменения в настраиваемую ячейку. У вас есть пользовательская ячейка, называемая DataSetCell в своем классе, которую вы можете сделать (код в быстром)

override var isSelected: Bool {
    didSet {
        if isSelected {
            changeStuff
        } else {
            changeOtherStuff
        }
    }
}

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