cellForItemAtIndexPath не вызывается с пользовательским UICollectionViewLayout

avatar
Peter
7 апреля 2018 в 23:31
411
1
1

Вот как я инициализирую collectionView:

self.collectionView = [[UICollectionView alloc]initWithFrame:self.frame collectionViewLayout:[[customLayout alloc]init]];
self.collectionView.dataSource = self;
self.collectionView.delegate = self;
//[self.collectionView setAlwaysBounceVertical:YES];
[self.collectionView setBackgroundColor:[UIColor blueColor]];
self.collectionView.decelerationRate = UIScrollViewDecelerationRateFast;
UINib *nibf = [UINib nibWithNibName:@"inviteUserCell" bundle:nil];
[self.collectionView registerNib:nibf forCellWithReuseIdentifier:@"user"];
[self addSubview:self.collectionView];

Чем у меня есть следующие методы делегата:

 - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView{
     NSLog(@"sections 1");
     return 1;
 }
 - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
     NSLog(@"items 40");
     return 40;
 }
 - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
     inviteUserCell *cell = (inviteUserCell *) [collectionView dequeueReusableCellWithReuseIdentifier:@"user" forIndexPath:indexPath];
     [cell setBackgroundColor:[UIColor whiteColor]];
     NSLog(@"CELL: %@", cell);
     return cell;
 }

В моем журнале я получаю sections 1 и items 40, но не получаю CELL: %@. Для моего подкласса UICollectionViewLayout у меня есть следующие методы.

 - (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds{
     return true;
 }
 - (CGSize)collectionViewContentSize{
     CGSize size = CGSizeMake(self.collectionView.frame.size.width, self.collectionView.frame.size.height);
     return size;
 }
 - (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect{
     NSArray *attributesInRect = [super layoutAttributesForElementsInRect:rect];
     for (UICollectionViewLayoutAttributes *cellAttributes in attributesInRect) {
        // [self modifyLayoutAttributes:cellAttributes];
          cellAttributes.size = CGSizeMake(10, 10);
          cellAttributes.center = CGPointMake(100, 100);
     }
     return attributesInRect;
 }
 - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{
     UICollectionViewLayoutAttributes *attributes = [super layoutAttributesForItemAtIndexPath:indexPath];
    // [self modifyLayoutAttributes:attributes]; 
      attributes.size = CGSizeMake(10, 10);
      attributes.center = CGPointMake(100, 100);
     NSLog(@"Attr: %@", attributes);    // this is null
     return attributes;
 }

Я не уверен, что я не вызываю или не устанавливаю, что вызовет cellForItemAtIndexPath.

Макет Желаемый макет — сетка 50x50 с вертикальной и горизонтальной прокруткой. Из чтения вокруг UICollectionViewFlowLayout возможен только вертикальный или горизонтальный скроллинг. Итак, я создал подкласс UICollectionViewLayout

Источник
Eric Murphey
8 апреля 2018 в 05:06
0

Вы создаете подклассы UICollectionViewFlowLayout или UICollectionViewLayout?

Rob
9 апреля 2018 в 08:36
0

«Желаемый макет представляет собой сетку 50x50 с вертикальной и горизонтальной прокруткой. Из чтения вокруг UICollectionViewFlowLayout возможен только вертикальный или горизонтальный скроллинг. Поэтому я создал подкласс UICollectionViewLayout». ... Да, это правильно. Для этого вам понадобится подкласс UICollectionViewLayout.

Ответы (1)

avatar
Rob
8 апреля 2018 в 07:16
1

Несколько замечаний:

  1. Ключевая проблема заключается в том, что layoutAttributesForElementsInRect должен возвращать массив атрибутов макета для всех ячеек, видимых в конкретном прямоугольнике. Если вы не вернете массив объектов атрибутов, по одному для каждой видимой ячейки, он не будет запрашивать cellForItemAt для создания каких-либо ячеек.

    Теперь то, как вы будете делать это для вашего конкретного макета, может отличаться, но, например, это находит все те, которые пересекаются с rect, вызывая layoutAttributesForItemAtIndexPath ​​для каждого из них и добавляя их к массиву.

  2. Ваш layoutAttributesForItemAtIndexPath указывает, что все ячейки должны иметь центр в 100, 100 в представлении коллекции и иметь размер 10, 10. Т.е. короче говоря, вы указываете, что все они находятся в одном и том же месте, перекрывая друг друга. Это немного любопытно. Обычно каждая ячейка содержит несколько уникальных center.

    Если вы хотите разместить их в сетке 50 x 50, вы можете сделать следующее:

    //  CustomLayout.m
    
    #import "CustomLayout.h"
    
    static const CGSize    kCellSize    = { 70, 30 };  // make whatever size you want
    static const CGFloat   kCellSpacing = 5;
    static const NSInteger kCellsPerRow = 50;
    
    @implementation CustomLayout
    
    - (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds{
        return true;
    }
    
    - (CGSize)collectionViewContentSize {
        NSInteger count = [self.collectionView numberOfItemsInSection:0];
        if (count == 0) return CGSizeZero;
    
        NSInteger numberOfColumns = MIN(kCellsPerRow, count);
        NSInteger numberOfRows = (count - 1) % kCellsPerRow + 1;
        return CGSizeMake(numberOfColumns * (kCellSize.width + kCellSpacing) + kCellSpacing,
                          numberOfRows * (kCellSize.height + kCellSpacing) + kCellSpacing);
    }
    
    - (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {
        NSMutableArray <UICollectionViewLayoutAttributes *> *attributes = [NSMutableArray array];
    
        NSInteger count = [self.collectionView numberOfItemsInSection:0];
        NSInteger numberOfRows = (count - 1) % kCellsPerRow + 1;
    
        NSInteger startColumn = MAX(0, floorf(rect.origin.x / (kCellSize.width + kCellSpacing)));
        NSInteger endColumn = MIN(kCellsPerRow, ceilf((rect.origin.x + rect.size.width) / (kCellSize.width + kCellSpacing)));
    
        NSInteger startRow = MAX(0, floorf(rect.origin.y / (kCellSize.height + kCellSpacing)));
        NSInteger endRow = MIN(numberOfRows, ceilf((rect.origin.y + rect.size.height) / (kCellSize.height + kCellSpacing)));
    
        for (NSInteger row = startRow; row < endRow; row++) {
            for (NSInteger column = startColumn; column < endColumn; column++) {
                NSInteger index = row * kCellsPerRow + column;
                if (index < count) {
                    [attributes addObject:[self layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:index inSection:0]]];
                }
            }
        }
    
        return attributes;
    }
    
    - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{
        UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
    
        NSInteger column = indexPath.item % kCellsPerRow;
        NSInteger row = indexPath.item / kCellsPerRow;
    
        attributes.size = kCellSize;
        attributes.center = CGPointMake(column * (kCellSize.width  + kCellSpacing) + kCellSpacing + kCellSize.width / 2,
                                        row    * (kCellSize.height + kCellSpacing) + kCellSpacing + kCellSize.height / 2);
    
        return attributes;
    }
    
    @end
    

    Очевидно, что если вам нужна сетка 50 x 50, то вы, вероятно, хотите, чтобы количество ячеек было 2500, а не 40.

  3. Я уверен, что это было опущено для краткости, но ваш фрагмент кода не демонстрирует настройку представления коллекции collectionViewLayout.

Во всяком случае, приведенное выше отобразило следующее представление коллекции:

enter image description here

Теперь я сделал свои ячейки достаточно большими, чтобы я мог поместить туда метку и показать indexPath.item ячейки, но сделать его любого размера, который вы хотите.

Peter
9 апреля 2018 в 07:31
0

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

Rob
9 апреля 2018 в 08:41
0

@Peter - см. пример представления коллекции сетки 50x50.