2017-02-21 37 views
1

Мне нужно создать UICollectionView с ячейками разных размеров (1x1, 2x2, 2x1, 3x2.5). У меня уже есть код для добавления ячеек в зависимости от того, какой размер они используют collectionView:layout:sizeForItemAtIndexPath:.Заказ ячеек в UICollectionView

Вот ожидаемый результат (ячейки 1 и 3 должны быть слева от ячейки 2):
enter image description here

Но текущий результат является:
enter image description here

Мой (некрасиво) текущий код (self.cellSize = ширина экрана/3):

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath { 

    CGFloat size = self.cellSize; 

    if (indexPath.item == 0) { 
     return CGSizeMake(self.cellSize * 3.f, self.cellSize * 2.5f); 
    } 

    if (indexPath.item == 1) { 
     return CGSizeMake(self.cellSize, self.cellSize); 
    } 

    if (indexPath.item == 2) { 
     return CGSizeMake(self.cellSize * 2.f, self.cellSize * 2.f); 
    } 

    if (indexPath.item == 3) { 
     return CGSizeMake(self.cellSize, self.cellSize); 
    } 

    if (indexPath.item == 4) { 
     return CGSizeMake(self.cellSize * 2.f, self.cellSize); 
    } 

    if (indexPath.item == 5) { 
     return CGSizeMake(self.cellSize, self.cellSize); 
    } 

    if (indexPath.item == 6) { 
     return CGSizeMake(self.cellSize, self.cellSize); 
    } 

    if (indexPath.item == 7) { 
     return CGSizeMake(self.cellSize * 2.f, self.cellSize); 
    } 

    if (indexPath.item == 8) { 
     return CGSizeMake(self.cellSize * 3.f, self.cellSize * 2.5f); 
    } 

    return CGSizeMake(size, size); 
} 

есть ли способ, чтобы указать, что ячейка 3 должен быть выше ячейки 1, но с левой стороны от с ell 2?

Я не хочу делать статический макет, который не изменится, потому что каждая ячейка может иметь разный размер в будущем. Например, ячейка 2 может быть размером 2x1 ...

Какой из лучших способов сделать это? Я ожидал указать на UICollectionViewLayout поток (например, «сверху»), но он не работает так ...

+2

Я думаю, что вам нужно подклассифицировать 'UICollectionViewLayout', где вы дадите каждой ячейке свой фрейм. – Larme

ответ

8

Пример примера путем подкласса UICollectionViewLayout. Все значения жестко закодированы, просто чтобы указать логику, лежащую в ее основе. Конечно, это можно было бы оптимизировать.

@interface CustomCollectionViewLayout() 

@property (nonatomic, strong) NSMutableDictionary *cellLayouts; 
@property (nonatomic, assign) CGSize    unitSize; 

@end 

@implementation CustomCollectionViewLayout 


-(id)initWithSize:(CGSize)size 
{ 
    self = [super init]; 
    if (self) 
    { 
    _unitSize = CGSizeMake(size.width/3,150); 
    _cellLayouts = [[NSMutableDictionary alloc] init]; 
    } 
    return self; 
} 
-(void)prepareLayout 
{ 

    for (NSInteger i = 0; i < [[self collectionView] numberOfItemsInSection:0]; i ++) 
    { 
    NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0]; 
    UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath]; 
    CGRect frame; 
    switch ([indexPath item]) 
    { 
     case 0: 
     frame = CGRectMake(0, 0, _unitSize.width*3, _unitSize.height*2.5); 
     break; 
     case 1: 
     frame = CGRectMake(0, _unitSize.height*2.5, _unitSize.width, _unitSize.height); 
     break; 
     case 2: 
     frame = CGRectMake(_unitSize.width, _unitSize.height*2.5, _unitSize.width*2, _unitSize.height*2); 
     break; 
     case 3: 
     frame = CGRectMake(0, _unitSize.height*2.5+_unitSize.height, _unitSize.width, _unitSize.height); 
     break; 
     case 4: 
     frame = CGRectMake(0, _unitSize.height*2.5+_unitSize.height+_unitSize.height, _unitSize.width*2, _unitSize.height); 
     break; 
     case 5: 
     frame = CGRectMake(_unitSize.width*2, _unitSize.height*2.5+_unitSize.height+_unitSize.height, _unitSize.width, _unitSize.height); 
     break; 
     case 6: 
     frame = CGRectMake(0, _unitSize.height*2.5+_unitSize.height+_unitSize.height+_unitSize.height, _unitSize.width, _unitSize.height); 
     break; 
     case 7: 
     frame = CGRectMake(_unitSize.width, _unitSize.height*2.5+_unitSize.height+_unitSize.height+_unitSize.height, _unitSize.width*2, _unitSize.height); 
     break; 
     case 8: 
     frame = CGRectMake(0, _unitSize.height*2.5+_unitSize.height+_unitSize.height+_unitSize.height+_unitSize.height, _unitSize.width*3, _unitSize.height*2.5); 
     break; 
     default: 
     frame = CGRectZero; 
     break; 
    } 
    [attributes setFrame:frame]; 
    [[self cellLayouts] setObject:attributes forKey:indexPath]; 
    } 
} 

-(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect 
{ 
    NSMutableArray *retAttributes = [[NSMutableArray alloc] init]; 
    for (NSIndexPath *anIndexPath in [self cellLayouts]) 
    { 
    UICollectionViewLayoutAttributes *attributes = [self cellLayouts][anIndexPath]; 
    if (CGRectIntersectsRect(rect, [attributes frame])) 
    { 
     [retAttributes addObject:attributes]; 
    } 
    } 
    return retAttributes; 
} 

-(UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath 
{ 

    return [self cellLayouts][indexPath]; 
} 

-(CGSize)collectionViewContentSize 
{ 
    return CGSizeMake(_unitSize.width*3, _unitSize.height*9); 
} 

@end 

Тогда вам просто нужно позвонить:

CustomCollectionViewLayout *layout = [[CustomCollectionViewLayout alloc] initWithSize:self.myCollectionView.bounds.frame.size]; 
[self.myCollectionView setCollectionViewLayout:layout]; 

Rendering: enter image description here

+0

Wooh! Он работает отлично. Большое спасибо. Я просто попробую переписать немного жестко закодированной части. – Jonathan

+0

@ Larme, это действительно помогает. Еще раз спасибо + 1 Проголосовал –

1

Вот это та же логика, как предложил Larme. Но с меньшим количеством жесткого кода и который дает вам возможность установить количество элементов, которые вы хотите, без добавления нового case:.

Я называю «узор» набором из 5 предметов. Поэтому сначала я определить постоянные значения:

#define MAX_COLUMN    3 // Max columns in the pattern 
#define MAX_LINE_PER_PATTERN 3 // Max lines in the pattern 
#define PATTERN_ITEM_COUNT  5 // Max items in the pattern 

Затем создать пользовательский макет с 2 свойствами NSMutableArray *layoutAttributes и CGFloat contentHeight, в котором мне нужно переопределить методы:

- (void)prepareLayout{ 
    [super prepareLayout]; 

    if (!self.layoutAttributes){ 
     self.layoutAttributes = [[NSMutableArray alloc] init]; 

     CGFloat cellWidth = self.collectionView.frame.size.width/MAX_COLUMN; 
     CGFloat cellHeight = cellWidth; 
     self.contentHeight = 0.f; 

     for (int item = 0 ; item < [self.collectionView numberOfItemsInSection:0] ; item ++){ 

      CGFloat width, height = 0.f; 
      CGFloat xPos, yPos = 0.f; 

      NSInteger patternCount = (NSInteger)((CGFloat)item/(CGFloat)PATTERN_ITEM_COUNT); 
      NSInteger currentIndex = item % PATTERN_ITEM_COUNT; 
      switch (currentIndex) { 
       case 0: 
       { 
        xPos = 0.f; 
        yPos = 0.f + MAX_LINE_PER_PATTERN * cellHeight * patternCount; 
        width = cellWidth; 
        height = cellHeight; 
        self.contentHeight += cellHeight; 
        break; 
       } 
       case 1: 
       { 
        xPos = cellWidth; 
        yPos = 0.f + MAX_LINE_PER_PATTERN * cellHeight * patternCount; 
        width = cellWidth * 2.f; 
        height = cellHeight * 2.f; 
        self.contentHeight += cellHeight; 
        break; 
       } 
       case 2: 
       { 
        xPos = 0.f; 
        yPos = cellHeight + MAX_LINE_PER_PATTERN * cellHeight * patternCount; 
        width = cellWidth; 
        height = cellHeight; 
        break; 
       } 
       case 3: 
       { 
        xPos = 0.f; 
        yPos = 2.f * cellHeight + MAX_LINE_PER_PATTERN * cellHeight * patternCount; 
        width = cellWidth * 2.f; 
        height = cellHeight; 
        self.contentHeight += cellHeight; 
        break; 
       } 
       case 4: 
       { 
        xPos = 2.f * cellWidth; 
        yPos = 2.f * cellHeight + MAX_LINE_PER_PATTERN * cellHeight * patternCount; 
        width = cellWidth; 
        height = cellHeight; 
        break; 
       } 
       default: 
        NSLog(@"error with index"); 
        break; 
      } 
      UICollectionViewLayoutAttributes *attr = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:[NSIndexPath indexPathForRow:item inSection:0]]; 
      attr.frame = CGRectMake(xPos, 
            yPos, 
            width, 
            height); 
      [self.layoutAttributes addObject:attr]; 
     } 
    } 
} 

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect{ 
    NSMutableArray *currentAttributes = [NSMutableArray new]; 
    for (UICollectionViewLayoutAttributes *attr in self.layoutAttributes) { 
     if (CGRectIntersectsRect(attr.frame, rect)) 
     { 
      [currentAttributes addObject:attr]; 
     } 
    } 
    return currentAttributes; 
} 

- (CGSize)collectionViewContentSize{ 
    return CGSizeMake(self.collectionView.frame.size.width, self.contentHeight); 
} 

Затем назначьте этот пользовательский макет в self.collectionView.collectionViewLayout и это оно. Вы можете найти более подробную информацию и оперативную версию here.