自定義UICollectionViewLayout

weixin_33912445發表於2017-05-16

效果如圖:


2204830-0f1d6da7b0378c64.gif

自定義UICollectionViewLayout 除了計算比較麻煩,其他控制還蠻方便
先貼關鍵程式碼,等有時間在寫註釋:

@interface WMTopUpLayout ()

/** 包含 header 和 footer */
@property (nonatomic, strong) NSMutableArray *layoutInfoArray;

/** 不包含 header 和 footer */
@property (nonatomic, strong) NSMutableArray *itemLayoutInfoArray;

@property (nonatomic, assign) CGSize contentSize;

@end

@implementation WMTopUpLayout

- (NSMutableArray *)layoutInfoArray {
    if (!_layoutInfoArray) {

        _layoutInfoArray = [[NSMutableArray alloc] init];

    }
    return _layoutInfoArray;
}

- (NSMutableArray *)itemLayoutInfoArray {
    if (!_itemLayoutInfoArray) {

        _itemLayoutInfoArray = [[NSMutableArray alloc] init];

    }
    return _itemLayoutInfoArray;
}


/** 
    The collection view calls -prepareLayout once at its first layout as the first message to the layout instance.
    The collection view calls -prepareLayout again after layout is invalidated and before requerying the layout information.
    Subclasses should always call super if they override.
 */
- (void)prepareLayout {

    [super prepareLayout];

    [self.layoutInfoArray removeAllObjects];

    [self.itemLayoutInfoArray removeAllObjects];

    NSInteger numberOfSection = [self.collectionView numberOfSections];

    for (int section = 0; section < numberOfSection; section ++) {

        NSInteger numberOfItems = [self.collectionView numberOfItemsInSection:section];

        NSMutableArray *tmp_layoutArray = [NSMutableArray array];

        NSMutableArray *tmp_itemArray = [NSMutableArray array];

        if (section == 2 && numberOfItems > 0) {

            UICollectionViewLayoutAttributes *headerAttr = [self layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader atIndexPath:[NSIndexPath indexPathForRow:0 inSection:section]];

            [tmp_layoutArray addObject:headerAttr];
        }

        for (int item = 0; item < numberOfItems; item ++) {

            NSIndexPath *indexPath = [NSIndexPath indexPathForRow:item inSection:section];

            UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForItemAtIndexPath:indexPath];

            [tmp_layoutArray addObject:attributes];

            [tmp_itemArray addObject:attributes];
        }

        if (section == 3 && numberOfItems > 0) {

            UICollectionViewLayoutAttributes *footerAttr = [self layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionFooter atIndexPath:[NSIndexPath indexPathForRow:0 inSection:section]];

            [tmp_layoutArray addObject:footerAttr];
        }

        // 把每一個section的每一個item的佈局資訊新增陣列
        if (tmp_layoutArray.count > 0) {

            [self.layoutInfoArray addObject:tmp_layoutArray];
        }

        if (tmp_itemArray.count > 0) {

            [self.itemLayoutInfoArray addObject:tmp_itemArray];
        }
    }

    UICollectionViewLayoutAttributes *attri = [[self.layoutInfoArray lastObject] lastObject];

    self.contentSize = CGSizeMake(_width_, CGRectGetMaxY(attri.frame));
}

/** 
    Subclasses must override this method and use it to return the width and height of the collection view’s content. These values represent the width and height of all the content, not just the content that is currently visible. The collection view uses this information to configure its own content size to facilitate scrolling.
 */
- (CGSize)collectionViewContentSize {

    return self.contentSize;
}

/**
 Returns the layout information for the item at the specified index path.

 This method updates the layout information as needed before returning the specified attributes. Always use this method to retrieve the layout attributes for items in the collection view. Do not query the layout object directly.
 */
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {

    UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];

    if (indexPath.section == 0) {

        attributes.frame = CGRectMake(0, 0, _width_, 44);

    } else if (indexPath.section == 1) {

        NSInteger row = indexPath.row;

        CGFloat margin_x = 5;

        CGFloat margin_y = 5;

        CGFloat item_w = (_width_ - 4 * margin_x) / 3;

        CGFloat item_h = item_w;

        CGFloat item_x = margin_x + (item_w + margin_x) * (row % 3);

        CGFloat item_y = 44 + 10 + (item_h + margin_y) * (row / 3);

        attributes.frame = CGRectMake(item_x, item_y, item_w, item_h);

    } else if (indexPath.section == 2) {

        UICollectionViewLayoutAttributes *attributesTmp = [[self.itemLayoutInfoArray lastObject] lastObject];

        CGFloat item_y = CGRectGetMaxY(attributesTmp.frame) + 44;

        attributes.frame = CGRectMake(0, item_y, _width_, 44);

    } else if (indexPath.section == 3) {

        UICollectionViewLayoutAttributes *attributesTmp = [[self.itemLayoutInfoArray lastObject] lastObject];

        CGFloat item_h = 44;

        CGFloat item_y = CGRectGetMaxY(attributesTmp.frame) + 10 + item_h * indexPath.row;

        attributes.frame = CGRectMake(0, item_y, _width_, item_h);

    }
    
    return attributes;
}

- (UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath {

    NSInteger numberOfItems = [self.collectionView numberOfItemsInSection:indexPath.section];

    if (elementKind == UICollectionElementKindSectionHeader) {

        UICollectionViewLayoutAttributes *header_attributes = [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader withIndexPath:indexPath];

        if (indexPath.section == 2 && numberOfItems > 0) {

            header_attributes = [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader withIndexPath:indexPath];

            UICollectionViewLayoutAttributes *attributesTmp = [[self.itemLayoutInfoArray lastObject] lastObject];

            CGFloat item_y = CGRectGetMaxY(attributesTmp.frame) + 5;

            header_attributes.frame = CGRectMake(0, item_y, _width_, 44);
            
        }

        return header_attributes;

    } else {

        UICollectionViewLayoutAttributes *footer_attributes = [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionFooter withIndexPath:indexPath];

        if (indexPath.section == 3 && numberOfItems > 0) {

            footer_attributes = [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionFooter withIndexPath:indexPath];

            UICollectionViewLayoutAttributes *attributesTmp = [[self.itemLayoutInfoArray lastObject] lastObject];

            NSInteger num_3 = [self.collectionView numberOfItemsInSection:3];

            CGFloat item_h = 44;

            CGFloat item_y = CGRectGetMaxY(attributesTmp.frame) + 10 + item_h * num_3;
            
            footer_attributes.frame = CGRectMake(0, item_y, _width_, 95);
            
        }

        return footer_attributes;
    }
}

/** 
    return an array layout attributes instances for all the views in the given rect
 */
- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {

    NSMutableArray *layoutAttributesArr = [NSMutableArray array];

    for (NSMutableArray *array in self.layoutInfoArray) {

        for (UICollectionViewLayoutAttributes *attributes in array) {

            [layoutAttributesArr addObject:attributes];
        }
    }

    return layoutAttributesArr;
}
@end

 不定期更新 不合適的地方 還請指點~ 感激不盡

相關文章