zoukankan      html  css  js  c++  java
  • UICollectionView 02

    一,UICollectionViewLayout布局的具体思路:

    • 设置itemSzie属性,它定义了每一个item的大小。在一个示例中通过设置layout的itemSize属性全局的设置了cell的尺寸。

      - (CGSize)collectionView:(UICollectionView *)collectionView
                       layout:(UICollectionViewLayout*)collectionViewLayout
        sizeForItemAtIndexPath:(NSIndexPath *)indexPath
    • 设置间隔

      间隔可以指定item之间的间隔和每一行之间的间隔。间隔和itemSzie一样,既有全局属性,也可以对每一个item设定:
      @property (nonatomic) CGFloat minimumLineSpacing;
      @property (nonatomic) CGFloat minimumInteritemSpacing;
      - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section;
      - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section;
    • 设定滚动方向

      typedef NS_ENUM(NSInteger, UICollectionViewScrollDirection) { 
         UICollectionViewScrollDirectionVertical, 
         UICollectionViewScrollDirectionHorizontal 
      };
    • 设置Header和Footer的尺寸

      设置Header和Footer的尺寸也分为全局和局部。在这里需要注意滚动的方向,滚动方向不同,header和footer的宽度和高度只有一个会起作用。垂直滚动时section间宽度为尺寸的高。
      @property (nonatomic) CGSize headerReferenceSize;
      @property (nonatomic) CGSize footerReferenceSize;
      - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section;
      - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section;
    • 设置内边距

      @property (nonatomic) UIEdgeInsets sectionInset;
      - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section

    二,用UICollectionView实现瀑布流

      实现瀑布流的方式有几种,但是比较简单的是通过UICollectionView,因为collectionView自己会实现cell的循环利用,所以自己不用实现循环利用的机制。瀑布就最重要的就是布局,要选取最短的那一列来排布,保证每一列之间的间距不会太大。

    实现步骤

    • 自定义继承自UICollectionViewLayout的子类来进行实现布局

      • 调用- (void)prepareLayout进行初始化
      • 重载- (CGSize)collectionViewContentSize返回内容的大小
      • 重载- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect方法返回rect中所有元素的布局属性,返回的是一个数组
      • 重载- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath方法返回对应的indexPath的位置的cell的布局属性。
      • 重载- (nullable UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath;方法返回对应indexPath的位置的追加视图的布局属性,如果没有就不用重载
      • 重载- (nullable UICollectionViewLayoutAttributes *)layoutAttributesForDecorationViewOfKind:(NSString*)elementKind atIndexPath:(NSIndexPath *)indexPath;方法返回对应indexPath的位置的装饰视图的布局属性,如果没有也不需要重载
      • 重载- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds;当边界发生改变时,是否应该刷新。

    三,自定义UICollectionViewLayout布局的示例代码

      用代理来实现对item的布局属性的控制

    • .h文件

      #import <UIKit/UIKit.h>
      @class LMHWaterFallLayout;
      
      @protocol  LMHWaterFallLayoutDeleaget<NSObject>
      @required
      /**
       * 每个item的高度
       */
      - (CGFloat)waterFallLayout:(LMHWaterFallLayout *)waterFallLayout heightForItemAtIndexPath:(NSUInteger)indexPath itemWidth:(CGFloat)itemWidth;
      
      @optional
      /**
       * 有多少列
       */
      - (NSUInteger)columnCountInWaterFallLayout:(LMHWaterFallLayout *)waterFallLayout;
      
      /**
       * 每列之间的间距
       */
      - (CGFloat)columnMarginInWaterFallLayout:(LMHWaterFallLayout *)waterFallLayout;
      
      /**
       * 每行之间的间距
       */
      - (CGFloat)rowMarginInWaterFallLayout:(LMHWaterFallLayout *)waterFallLayout;
      
      /**
       * 每个item的内边距
       */
      - (UIEdgeInsets)edgeInsetdInWaterFallLayout:(LMHWaterFallLayout *)waterFallLayout;
      
      @end
      
      @interface LMHWaterFallLayout : UICollectionViewLayout
      /** 代理 */
      @property (nonatomic, weak) id<LMHWaterFallLayoutDeleaget> delegate;
      
      @end
    • .m文件

      #import "LMHWaterFallLayout.h"
      
      /** 默认的列数    */
      static const CGFloat LMHDefaultColunmCount = 3;
      /** 每一列之间的间距    */
      static const CGFloat LMHDefaultColunmMargin = 10;
      /** 每一行之间的间距    */
      static const CGFloat LMHDefaultRowMargin = 10;
      
      /** 内边距    */
      static const UIEdgeInsets LMHDefaultEdgeInsets = {10,10,10,10};
      @interface LMHWaterFallLayout()
      /** 存放所有的布局属性 */
      @property (nonatomic, strong) NSMutableArray * attrsArr;
      /** 存放所有列的当前高度 */
      @property (nonatomic, strong) NSMutableArray *columnHeights;
      /** 内容的高度 */
      @property (nonatomic, assign) CGFloat contentHeight;
      
      - (NSUInteger)colunmCount;
      - (CGFloat)columnMargin;
      - (CGFloat)rowMargin;
      - (UIEdgeInsets)edgeInsets;
      
      @end
      
      @implementation LMHWaterFallLayout
      
      #pragma mark 懒加载
      - (NSMutableArray *)attrsArr{
          if (!_attrsArr) {
              _attrsArr = [NSMutableArray array];
          }
          return _attrsArr;
      }
      
      - (NSMutableArray *)columnHeights{
          if (!_columnHeights) {
              _columnHeights = [NSMutableArray array];
          }
          return _columnHeights;
      }
      
      #pragma mark - 数据处理
      /**
       * 列数
       */
      - (NSUInteger)colunmCount{
          
          if ([self.delegate respondsToSelector:@selector(columnCountInWaterFallLayout:)]) {
              return [self.delegate columnCountInWaterFallLayout:self];
          }else{
              return LMHDefaultColunmCount;
          }
      }
      
      /**
       * 列间距
       */
      - (CGFloat)columnMargin{
          if ([self.delegate respondsToSelector:@selector(columnMarginInWaterFallLayout:)]) {
              return [self.delegate columnMarginInWaterFallLayout:self];
          }else{
              return LMHDefaultColunmMargin;
          }
      }
      
      /**
       * 行间距
       */
      - (CGFloat)rowMargin{
          if ([self.delegate respondsToSelector:@selector(rowMarginInWaterFallLayout:)]) {
              return [self.delegate rowMarginInWaterFallLayout:self];
          }else{
              return LMHDefaultRowMargin;
          }
      }
      
      /**
       * item的内边距
       */
      - (UIEdgeInsets)edgeInsets{
          if ([self.delegate respondsToSelector:@selector(edgeInsetdInWaterFallLayout:)]) {
              return [self.delegate edgeInsetdInWaterFallLayout:self];
          }else{
              return LMHDefaultEdgeInsets;
          }
      }
      
      /**
       * 初始化
       */
      - (void)prepareLayout{
          [super prepareLayout];
          self.contentHeight = 0;
          // 清除之前计算的所有高度
          [self.columnHeights removeAllObjects];
          // 设置每一列默认的高度
          for (NSInteger i = 0; i < LMHDefaultColunmCount ; i ++) {
              [self.columnHeights addObject:@(LMHDefaultEdgeInsets.top)];
          }
          // 清楚之前所有的布局属性
          [self.attrsArr removeAllObjects];
          // 开始创建每一个cell对应的布局属性
          NSInteger count = [self.collectionView numberOfItemsInSection:0];
          for (int i = 0; i < count; i++) {
              // 创建位置
              NSIndexPath * indexPath = [NSIndexPath indexPathForItem:i inSection:0];
              // 获取indexPath位置上cell对应的布局属性
              UICollectionViewLayoutAttributes * attrs = [self layoutAttributesForItemAtIndexPath:indexPath];
              [self.attrsArr addObject:attrs];
          }    
      }
      
      /**
       * 返回indexPath位置cell对应的布局属性
       */
      - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{
          // 创建布局属性
          UICollectionViewLayoutAttributes * attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
          //collectionView的宽度
          CGFloat collectionViewW = self.collectionView.frame.size.width;
          // 设置布局属性的frame
          CGFloat cellW = (collectionViewW - self.edgeInsets.left - self.edgeInsets.right - (self.colunmCount - 1) * self.columnMargin) / self.colunmCount;
          CGFloat cellH = [self.delegate waterFallLayout:self heightForItemAtIndexPath:indexPath.item itemWidth:cellW];
          // 找出最短的那一列
          NSInteger destColumn = 0;
          CGFloat minColumnHeight = [self.columnHeights[0] doubleValue];
          
          for (int i = 1; i < LMHDefaultColunmCount; i++) {
              // 取得第i列的高度
              CGFloat columnHeight = [self.columnHeights[i] doubleValue];
              
              if (minColumnHeight > columnHeight) {
                  minColumnHeight = columnHeight;
                  destColumn = i;
              }
          }
          
          CGFloat cellX = self.edgeInsets.left + destColumn * (cellW + self.columnMargin);
          CGFloat cellY = minColumnHeight;
          if (cellY != self.edgeInsets.top) {
              
              cellY += self.rowMargin;
          }
      
          attrs.frame = CGRectMake(cellX, cellY, cellW, cellH);
          
          // 更新最短那一列的高度
          self.columnHeights[destColumn] = @(CGRectGetMaxY(attrs.frame));
          
          // 记录内容的高度 - 即最长那一列的高度
          CGFloat maxColumnHeight = [self.columnHeights[destColumn] doubleValue];
          if (self.contentHeight < maxColumnHeight) {
              self.contentHeight = maxColumnHeight;
          }
          return attrs;
      }
      
      /**
       * 决定cell的布局属性
       */
      - (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect{
          return self.attrsArr;
      }
      
      /**
       * 内容的高度
       */
      - (CGSize)collectionViewContentSize{
          return CGSizeMake(0, self.contentHeight + self.edgeInsets.bottom);
      }
    • 结果

    四,demo及注意  

      1.瀑布流中自定的cell时,不能使用纯frame布局,这是因为cell存在复用的问题。如果在初始化方法中直接使用frame布局,就会在复用cell时,造成cell内容与cell的控件存在混乱。
      2.demo

  • 相关阅读:
    【WP8.1】富文本
    【WP8.1】WebView笔记
    【WP8】扩展CM的WindowManager
    随笔:关于关于
    <正则吃饺子> :关于微信支付的简单总结说明(二)
    <正则吃饺子> :关于微信支付的简单总结说明(一)
    <正则吃饺子> :关于Collections中 比较器的简单使用
    <正则吃饺子> :关于 Matcher 的 replaceAll 的简单使用
    <正则吃饺子> :关于oracle 中 with的简单使用
    <正则吃饺子> :关于oracle 中 exists 、not exists 的简单使用
  • 原文地址:https://www.cnblogs.com/lxlx1798/p/13193648.html
Copyright © 2011-2022 走看看