iOS

iOS- 瀑布流实现

2017-02-28  本文已影响61人  小行为

今天讲讲大名鼎鼎的瀑布流,说实话这个已经很久了但是才想起把它写出来,先来个效果图看下吧....

前方高能!!! 注意!!!

屏幕快照 2017-02-28 20.08.44.png

不是让你们看胸的,切记......

模块化了一下瀑布流类,好吧上代码....


屏幕快照 2017-02-28 20.11.25.png

这是在外部调用它,是不是很方便,简简单单4行代码搞定.... 好了讲完了....

`U}(NMJSPP$O(R`T%YG5F3A.jpg

(好吧... 本来准备空白下一篇才展示出来但是不知道markDown怎么实现... 苦逼....)
你以为你李哥是这么不靠谱个人么?这就完了?这我出门不得挨揍啊....

屏幕快照 2017-02-28 20.18.36.png

这是封装好了留给外部调用的有几个参数是我们这边要用的 代码看下应该都能知道是做什么的..继续看实现部分...

- (instancetype)initWithFrame:(CGRect)frame ownerController:(UIViewController *)viewcontroller supView:(UIView *)supView
{
    self = [super init];
    if (self) {
        [JWEvent defaultJWEvent].hobbyGalleryTimesOfOnce = 0;
        [JWEvent defaultJWEvent].tattooGalleryTimesOfOnce = 0;
        loadBackImgColor = [NSArray arrayWithObjects:@(0x8c946b),@(0xf2e6bc),@(0xa56452),@(0xc2c2c2),@(0x837467),@(0xe5bbbc),@(0xcbb7a6),@(0xbddecd),@(0xffffff),@(0x000f05),@(0x637b7b),@(0x7c201f), nil];
        _superVc = viewcontroller;
        _dataListArray = [[NSMutableArray alloc]init];
        layout = [[JWWaterLayout alloc]init];
        
        _parm = [[NSMutableDictionary alloc]init];
        [_parm setObject:@(20) forKey:@"limit"];
        
        _listCollection = [[UICollectionView alloc]initWithFrame:frame collectionViewLayout:layout];
        
        [_listCollection registerClass:[WaterFallHeader class]  forSupplementaryViewOfKind:WaterFallSectionHeader withReuseIdentifier:@"WaterFallSectionHeader"];

        [_listCollection registerClass:[JWCollectionViewCell class] forCellWithReuseIdentifier:@"squareListcell"];
        
        _listCollection.dataSource = self;
        _listCollection.delegate = self;
        _listCollection.backgroundColor = [UIColor whiteColor];
        [supView addSubview:_listCollection];
        currentUpdated = nil;
        
        self.backImage = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 120, 160)];
        self.backImage.contentMode = UIViewContentModeCenter;
        self.backImage.centerX = SCREENWIDTH * 0.5;
        self.backImage.centerY = SCREENHEIGHT * 0.5;
        self.backImage.image = [UIImage imageNamed:@"img_no_content"];
        _listCollection.backgroundView = self.backImage;
    
    }
    return self;
}


- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
    return _dataListArray.count;
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString* identifier = @"squareListcell";
    JWCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
    JWCollectionItem *item = _dataListArray[indexPath.row];
    if (item) {
        [cell setDataItem:_dataListArray[indexPath.row] withColor:RGBCOLOR_HEX(0xeeeeee)];

    }
    return cell;

}

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
    
    JWCollectionItem *item = [_dataListArray objectAtIndex:indexPath.row];

    return item.squareListSize;
}


- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
    if ([[User defaultUser].item.sector integerValue] == 30) {
        [JWEvent defaultJWEvent].tattooGalleryTimesOfOnce++;
    }else{
        [JWEvent defaultJWEvent].hobbyGalleryTimesOfOnce++;
    }

    if ([collectionView isHeaderRefreshing] || [collectionView isFooterRefreshing])return;
    JWCollectionItem *item = (JWCollectionItem *)[_dataListArray objectAtIndex:indexPath.row];
    
    if (!item._id || !_superVc) {
        return;
    }
    ImageFeedInfoDetailViewController *detailVC = [[ImageFeedInfoDetailViewController alloc]initWithQuery:@{@"feedId":item._id}];
    detailVC.isTuKuPush = YES;
    [_superVc.navigationController pushViewController:detailVC animated:YES];
    
    
}
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout heightForHeaderInSection:(NSInteger)section
{
    if (_headView) {
       return  _headView.height;
    }
    return 0;
}
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
{
    UICollectionReusableView *reusableView = nil;

    if ([kind isEqualToString:WaterFallSectionHeader] && _headView)
    {
        reusableView = [collectionView dequeueReusableSupplementaryViewOfKind:kind                                                          withReuseIdentifier:@"WaterFallSectionHeader" forIndexPath:indexPath];
        WaterFallHeader* header = [[WaterFallHeader alloc]initWithFrame:CGRectMake(0, 0, SCREENWIDTH, _headView.height) withSubView:_headView];
        [reusableView addSubview:header];
        return reusableView;
    }
    return nil;
}

其实这些都没什么的,瀑布流主要是Layout计算高度 所以我重写了Layout..

#pragma mark - Methods to Override
- (void)prepareLayout
{
    [super prepareLayout];
    
    NSInteger numberOfSections = [self.collectionView numberOfSections];
    if (numberOfSections == 0)
    {
        return;
    }
    
    self.delegate = (id <JWWaterLayoutDelegate> )self.collectionView.delegate;
    
    NSInteger idx = 0;
    
    CGFloat width = self.collectionView.frame.size.width - self.sectionInset.left - self.sectionInset.right;
    
    self.itemWidth = floorf((width - (self.columnCount - 1) * self.minimumColumnSpacing) / self.columnCount);
    
    [self.headersAttribute removeAllObjects];
    [self.footersAttrubite removeAllObjects];
    [self.unionRects removeAllObjects];
    [self.columnHeights removeAllObjects];
    [self.allItemAttributes removeAllObjects];
    if (self.sectionItemAttributes) {
        [self.sectionItemAttributes removeAllObjects];
    }
    
    for (idx = 0; idx < self.columnCount; idx++)
    {
        [self.columnHeights addObject:@(0)];
    }
    
    // Create attributes
    CGFloat top = 0;
    UICollectionViewLayoutAttributes *attributes;
    
    for (NSInteger section = 0; section < numberOfSections; ++section)
    {
        /*
         * 1. Section header
         */
        CGFloat headerHeight;
        if ([self.delegate respondsToSelector:@selector(collectionView:layout:heightForHeaderInSection:)])
        {
            headerHeight = [self.delegate collectionView:self.collectionView layout:self heightForHeaderInSection:section];
        }
        else
        {
            headerHeight = self.headerHeight;
        }
        
        if (headerHeight > 0)
        {
            attributes = [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:WaterFallSectionHeader withIndexPath:[NSIndexPath indexPathForItem:0 inSection:section]];
            attributes.frame = CGRectMake(0, top, self.collectionView.frame.size.width, headerHeight);
            
            self.headersAttribute[@(section)] = attributes;
            [self.allItemAttributes addObject:attributes];
            
            top = CGRectGetMaxY(attributes.frame);
        }
        
        top += self.sectionInset.top;
        for (idx = 0; idx < self.columnCount; idx++)
        {
            self.columnHeights[idx] = @(top);
        }
        
        /*
         * 2. Section items
         */
        NSInteger itemCount = [self.collectionView numberOfItemsInSection:section];
        NSMutableArray *itemAttributes = [NSMutableArray arrayWithCapacity:itemCount];
        
        // Item will be put into shortest column.
        for (idx = 0; idx < itemCount; idx++)
        {
            NSIndexPath *indexPath = [NSIndexPath indexPathForItem:idx inSection:section];
            CGSize itemSize = [self.delegate collectionView:self.collectionView layout:self sizeForItemAtIndexPath:indexPath];
//            CGFloat itemHeight = floorf(itemSize.height * self.itemWidth / itemSize.width);
            CGFloat itemHeight = itemSize.height;
            NSUInteger columnIndex = [self shortestColumnIndex];
            CGFloat xOffset = self.sectionInset.left + (self.itemWidth + self.minimumColumnSpacing) * columnIndex;
            CGFloat yOffset = [self.columnHeights[columnIndex] floatValue];
            
            attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
            attributes.frame = CGRectMake(xOffset, yOffset, self.itemWidth, itemHeight);
            [itemAttributes addObject:attributes];
            [self.allItemAttributes addObject:attributes];
            self.columnHeights[columnIndex] = @(CGRectGetMaxY(attributes.frame) + self.minimumInteritemSpacing);
        }
        
        [self.sectionItemAttributes addObject:itemAttributes];
        
        /*
         * Section footer
         */
        CGFloat footerHeight;
        NSUInteger columnIndex = [self longestColumnIndex];
        top = [self.columnHeights[columnIndex] floatValue] - self.minimumInteritemSpacing + self.sectionInset.bottom;
        
        if ([self.delegate respondsToSelector:@selector(collectionView:layout:heightForFooterInSection:)])
        {
            footerHeight = [self.delegate collectionView:self.collectionView layout:self heightForFooterInSection:section];
        }
        else
        {
            footerHeight = self.footerHeight;
        }
        
        if (footerHeight > 0)
        {
            attributes = [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:WaterFallSectionFooter withIndexPath:[NSIndexPath indexPathForItem:0 inSection:section]];
            attributes.frame = CGRectMake(0, top, self.collectionView.frame.size.width, footerHeight);
            
            self.footersAttrubite[@(section)] = attributes;
            [self.allItemAttributes addObject:attributes];
            
            top = CGRectGetMaxY(attributes.frame);
        }
        
        for (idx = 0; idx < self.columnCount; idx++)
        {
            self.columnHeights[idx] = @(top);
        }
    } // end of for (NSInteger section = 0; section < numberOfSections; ++section)
    
    // Build union rects
    idx = 0;
    NSInteger itemCounts = [self.allItemAttributes count];
    while (idx < itemCounts)
    {
        CGRect rect1 = ((UICollectionViewLayoutAttributes *)self.allItemAttributes[idx]).frame;
        idx = MIN(idx + unionSize, itemCounts) - 1;
        CGRect rect2 = ((UICollectionViewLayoutAttributes *)self.allItemAttributes[idx]).frame;
        [self.unionRects addObject:[NSValue valueWithCGRect:CGRectUnion(rect1, rect2)]];
        idx++;
    }
}

- (CGSize)collectionViewContentSize
{
    NSInteger numberOfSections = [self.collectionView numberOfSections];
    if (numberOfSections == 0)
    {
        return CGSizeZero;
    }
    
    CGSize contentSize = self.collectionView.bounds.size;
    contentSize.height = [self.columnHeights[0] floatValue];
    
    return contentSize;
}

- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)path
{
    if (path.section >= [self.sectionItemAttributes count])
    {
        return nil;
    }
    if (path.item >= [self.sectionItemAttributes[path.section] count])
    {
        return nil;
    }
    return (self.sectionItemAttributes[path.section])[path.item];
}

- (UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
{
    UICollectionViewLayoutAttributes *attribute = nil;
    if ([kind isEqualToString:WaterFallSectionHeader])
    {
        attribute = self.headersAttribute[@(indexPath.section)];
    } else if ([kind isEqualToString:WaterFallSectionFooter])
    {
        attribute = self.footersAttrubite[@(indexPath.section)];
    }
    return attribute;
}

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
    NSInteger i;
    NSInteger begin = 0, end = self.unionRects.count;
    NSMutableArray *attrs = [NSMutableArray array];
    
    for (i = 0; i < self.unionRects.count; i++)
    {
        if (CGRectIntersectsRect(rect, [self.unionRects[i] CGRectValue]))
        {
            begin = i * unionSize;
            break;
        }
    }
    for (i = self.unionRects.count - 1; i >= 0; i--)
    {
        if (CGRectIntersectsRect(rect, [self.unionRects[i] CGRectValue]))
        {
            end = MIN((i + 1) * unionSize, self.allItemAttributes.count);
            break;
        }
    }
    for (i = begin; i < end; i++)
    {
        UICollectionViewLayoutAttributes *attr = self.allItemAttributes[i];
        if (CGRectIntersectsRect(rect, attr.frame))
        {
            [attrs addObject:attr];
        }
    }
    
    return [NSArray arrayWithArray:attrs];
}

- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{
    CGRect oldBounds = self.collectionView.bounds;
    if (CGRectGetWidth(newBounds) != CGRectGetWidth(oldBounds) ||
        CGRectGetHeight(newBounds) != CGRectGetHeight(oldBounds))
    {
        return YES;
    }
    return NO;
}

#pragma mark - Private Methods

/**
 *  Find the shortest column.
 *
 *  @return index for the shortest column
 */
- (NSUInteger)shortestColumnIndex
{
    __block NSUInteger index = 0;
    __block CGFloat shortestHeight = MAXFLOAT;
    
    if (self.columnHeights) {
        [self.columnHeights enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop)
         {
             if (obj) {
                 CGFloat height = [obj floatValue];
                 if (height < shortestHeight)
                 {
                     shortestHeight = height;
                     index = idx;
                 }

             }
     
         }];
    }
    
    return index;
}

/**
 *  Find the longest column.
 *
 *  @return index for the longest column
 */
- (NSUInteger)longestColumnIndex
{
    __block NSUInteger index = 0;
    __block CGFloat longestHeight = 0;
    
    [self.columnHeights enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop)
     {
         CGFloat height = [obj floatValue];
         if (height > longestHeight)
         {
             longestHeight = height;
             index = idx;
         }
     }];
    
    return index;
}


这是计算item大小的算出最高和最矮的,代码没有全部沾出.. 但是看下流程应该都能想起该如何去实现....
代码就不附上了...

上一篇下一篇

猜你喜欢

热点阅读