UICollectionView中的UICollectionViewFlowLayout - 每个部分中的动态单元格数

时间:2015-03-04 15:15:35

标签: ios objective-c uicollectionview uicollectionviewlayout

所以我试图使用UICollectionView构建一个类似EPG的视图。这是我第一次体验这个框架。类似于this

我很快注意到我需要实现一个自定义布局来实现这个EPG。我遵循了guide

我需要"坚持"集合视图的第一列和第一行,并使EPG水平和垂直滚动。虽然教程很棒,但我在修改它时遇到了问题。即,教程声明每个部分中的列具有相等的宽度。而我要求我的列在每个部分都是动态的。例如:每行本身就是一个部分,因此每行的高度将保持不变,但每行中的单元格将具有不同的宽度,具体取决于要在每个单独的集合视图单元格中显示的程序的长度。

任何人都可以通过以下代码帮助我或指出正确的方向:

- (void)prepareLayout
{
    if ([self.collectionView numberOfSections] == 0) {
        return;
    }

    NSUInteger column = 0; // Current column inside row
    CGFloat xOffset = 0.0;
    CGFloat yOffset = 0.0;
    CGFloat contentWidth = 0.0; // To determine the contentSize
    CGFloat contentHeight = 0.0; // To determine the contentSize

    if (self.itemAttributes.count > 0) { // We don't enter in this if statement the first time, we enter the following times
        for (int section = 0; section < [self.collectionView numberOfSections]; section++) {
            NSUInteger numberOfItems = [self.collectionView numberOfItemsInSection:section];
            for (NSUInteger index = 0; index < numberOfItems; index++) {
                if (section != 0 && index != 0) { // This is a content cell that shouldn't be sticked
                    continue;
                }
                UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:index inSection:section]];
                if (section == 0) { // We stick the first row
                    CGRect frame = attributes.frame;
                    frame.origin.y = self.collectionView.contentOffset.y;
                    attributes.frame = frame;

                }
                if (index == 0) { // We stick the first column
                    CGRect frame = attributes.frame;
                    frame.origin.x = self.collectionView.contentOffset.x;
                    attributes.frame = frame;
                }
            }
        }

        return;
    }

    // The following code is only executed the first time we prepare the layout
    self.itemAttributes = [@[] mutableCopy];
    self.itemsSize = [@[] mutableCopy];

    // Tip: If we don't know the number of columns we can call the following method and use the NSUInteger object instead of the NUMBEROFCOLUMNS macro
    // NSUInteger numberOfItems = [self.collectionView numberOfItemsInSection:section];

    // We calculate the item size of each column
    if (self.itemsSize.count != NUMBEROFCOLUMNS) {
        [self calculateItemsSize];
    }

    // We loop through all items
    for (int section = 0; section < [self.collectionView numberOfSections]; section++) {
        NSMutableArray *sectionAttributes = [@[] mutableCopy];

        for (NSUInteger index = 0; index < NUMBEROFCOLUMNS; index++) {
            CGSize itemSize = [self.itemsSize[index] CGSizeValue];

            // We create the UICollectionViewLayoutAttributes object for each item and add it to our array.
            // We will use this later in layoutAttributesForItemAtIndexPath:
            NSIndexPath *indexPath = [NSIndexPath indexPathForItem:index inSection:section];
            UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
            attributes.frame = CGRectIntegral(CGRectMake(xOffset, yOffset, itemSize.width, itemSize.height));

            if (section == 0 && index == 0) {
                attributes.zIndex = 1024; // Set this value for the first item (Sec0Row0) in order to make it visible over first column and first row
            } else if (section == 0 || index == 0) {
                attributes.zIndex = 1023; // Set this value for the first row or section in order to set visible over the rest of the items
            }
            if (section == 0) {
                CGRect frame = attributes.frame;
                frame.origin.y = self.collectionView.contentOffset.y;
                attributes.frame = frame; // Stick to the top
            }
            if (index == 0) {
                CGRect frame = attributes.frame;
                frame.origin.x = self.collectionView.contentOffset.x;
                attributes.frame = frame; // Stick to the left
            }

            [sectionAttributes addObject:attributes];

            xOffset = xOffset+itemSize.width;
            column++;

            // Create a new row if this was the last column
            if (column == NUMBEROFCOLUMNS) {
                if (xOffset > contentWidth) {
                    contentWidth = xOffset;
                }

                // Reset values
                column = 0;
                xOffset = 0;
                yOffset += itemSize.height;
            }
        }
        [self.itemAttributes addObject:sectionAttributes];
    }

    // Get the last item to calculate the total height of the content
    UICollectionViewLayoutAttributes *attributes = [[self.itemAttributes lastObject] lastObject];
    contentHeight = attributes.frame.origin.y+attributes.frame.size.height;
    self.contentSize = CGSizeMake(contentWidth, contentHeight);
}

- (CGSize)collectionViewContentSize
{
    return self.contentSize;
}

- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
    return self.itemAttributes[indexPath.section][indexPath.row];
}


- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
    NSMutableArray *attributes = [@[] mutableCopy];

    for (NSArray *section in self.itemAttributes) {

        [attributes addObjectsFromArray:[section filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(UICollectionViewLayoutAttributes *evaluatedObject, NSDictionary *bindings) {
            return CGRectIntersectsRect(rect, [evaluatedObject frame]);
        }]]];
    }

    return attributes;
}

- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{
    return YES; // Set this to YES to call prepareLayout on every scroll
}


- (CGSize)sizeForItemWithColumnIndex:(NSUInteger)columnIndex
{
    NSLog(@"sizeForItemWithColumnIndex");
    NSString *text;
    for (NSUInteger index = 0; index < NUMBEROFCOLUMNS; index++) {


        text = @"asdf";

    }


    CGSize size = [text sizeWithAttributes: @{NSFontAttributeName:[UIFont fontWithName:@"HelveticaNeue" size:15]}];
    NSLog(@"Size of %@", NSStringFromCGSize(size));
    if (columnIndex == 0) {
        size.width += 1; // In our design the first column should be the widest one
    }
    return CGSizeMake([@(size.width + 50) floatValue], 40); // Extra space of 9px for all the items
}



- (void)calculateItemsSize
{
    NSLog(@"calculate item size");

    for (NSUInteger index = 0; index < NUMBEROFCOLUMNS; index++) {

        if (self.itemsSize.count <= index) {

            CGSize itemSize = [self sizeForItemWithColumnIndex:index];

            NSValue *itemSizeValue = [NSValue valueWithCGSize:itemSize];

            [self.itemsSize addObject:itemSizeValue];
        }
    }
}

@end

1 个答案:

答案 0 :(得分:0)

好的,我自己从头开始回答这个问题。在准备布局之前,我将程序和通道信息的xml Web服务加载到SQLite数据库中。使用第三方库FMDB拉出行,并根据每个程序运行的分钟数计算每个单元的帧(xPos,yPos,cellWidth,cellHeight)。 (除了包含时间轴的第一部分,以及每个部分中包含每个特定通道的图标的第一个项目。)我保存到字典中的每个计算都可以参考布局。

- (void)calculateFramesForAllPrograms {

    NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:0];
    NSInteger counterForDatabaseRow = 0;

    for (NSInteger section = 0; section < numOfChannels + EXTRA_ROW_FOR_TIME; section++) {

        if(section==0){

            for (NSInteger items = 0; items <=HOURS_IN_A_DAY + EXTRA_ROW_FOR_TIME; items++){

                if(items==0){

                    CGRect frame = CGRectMake(xPos, yPos, TILE_HEIGHT, TILE_HEIGHT);
                    NSString *rectString = NSStringFromCGRect(frame);
                    indexPath = [NSIndexPath indexPathForItem:items inSection:section];
                    //NSLog(@"INDEX PATH %@", indexPath);
                    self.frameInfo[indexPath] = rectString;

                    xPos = xPos+TILE_HEIGHT;

                }else {

                    CGRect frame = CGRectMake(xPos, yPos, self.timeTile.width, self.timeTile.height);
                    NSString *rectString = NSStringFromCGRect(frame);
                    indexPath = [NSIndexPath indexPathForItem:items inSection:section];
                    //NSLog(@"INDEX PATH %@", indexPath);
                    self.frameInfo[indexPath] = rectString;

                    xPos = xPos+self.timeTile.width;

                }
            }

        } else {

            xPos = 0;
            //NSLog(@"SECTION COUNT: %lu", section);
            NSString *query = [NSString stringWithFormat:@"SELECT COUNT(channel_code) AS channels FROM epg WHERE channel_code='%@", self.channelCodes[section-EXTRA_ROW_FOR_TIME]];
            NSString *completedQuery = [query stringByAppendingString:@"'"];
            //NSLog(@"string %@", completedQuery);
            db = [FMDatabase databaseWithPath:[Utility getDatabasePath]];
            [db open];
            NSUInteger itemCount = [db intForQuery:completedQuery];

                for (NSInteger item = 0; item <itemCount+EXTRA_ROW_FOR_TIME; item++) {

                    //NSLog(@"Item: %lu Item: %lu", item, itemCount);

                    if(item==0){

                        CGRect frame = CGRectMake(xPos, yPos, TILE_HEIGHT, TILE_HEIGHT);
                        NSString *rectString = NSStringFromCGRect(frame);
                        indexPath = [NSIndexPath indexPathForItem:item inSection:section];
                        //NSLog(@"INDEX PATH %@", indexPath);
                        self.frameInfo[indexPath] = rectString;

                        xPos = xPos+TILE_HEIGHT;

                    }else {

                        if (item==itemCount){
                            //NSLog(@"LAST ITEM %@", self.endTimes[counter]);
                            isLastItem = TRUE;
                        }

                        CGSize tileSize = [self sizeForProgramTile:counterForDatabaseRow];
                        //NSLog(@"TILE SIZE RETURNED %@", NSStringFromCGSize(tileSize));
                        CGRect frame = CGRectMake(xPos, yPos, tileSize.width, tileSize.height);
                        NSString *rectString = NSStringFromCGRect(frame);
                        indexPath = [NSIndexPath indexPathForItem:item inSection:section];
                        //NSLog(@"INDEX PATH %@", indexPath);
                        self.frameInfo[indexPath] = rectString;

                        xPos = xPos+tileSize.width;

                        counterForDatabaseRow++;

                    }

                }
        }

        yPos += TILE_HEIGHT;
    }
    //NSLog(@"FRAME INFO %@", self.frameInfo);
}