美文网首页iOS
iOS- 瀑布流实现

iOS- 瀑布流实现

作者: 小行为 | 来源:发表于2017-02-28 20:31 被阅读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大小的算出最高和最矮的,代码没有全部沾出.. 但是看下流程应该都能想起该如何去实现....
    代码就不附上了...

    相关文章

      网友评论

        本文标题:iOS- 瀑布流实现

        本文链接:https://www.haomeiwen.com/subject/pwhfgttx.html