概述


  • 对于经常使用的控件或类,通常将其分装为一个单独的类来供外界使用,以此达到事半功倍的效果
  • 由于分装的类不依赖于其他的类,所以若要使用该类,可直接将该类拖进项目文件即可
  • 在进行分装的时候,通常需要用到代理设计模式

代理设计模式


  • 代理设计模式的组成

    • 客户类(通常作为代理)

      • 通常委托这是角色来完成业务逻辑
    • 真实角色

      • 将客户类的业务逻辑转化为方法列表,即代理协议
    • 代理协议

      • 定义了需要实现的业务逻辑
      • 定义了一组方法列表,包括必须实现的方法或选择实现的方法
      • 代理协议是代理对象所要遵循一组规则
    • 代理角色

      • 若要作为代理,需要遵守代理协议,并且实现必须实现的代理方法
      • 代理角色可以通过调用代理协议中的方法完成业务逻辑,也可以附加自己的操作
  • 文字描述通常是抽象的,一下通过图示来阐述代理设计模式

自定义布局类的封装


  • 业务逻辑

    • 如图

  • 布局每个cell的业务逻辑

    • 由于设置每个cell的布局属性的业务逻辑较复杂,特附上如下思维导图

  • 封装思路封装需要根据客户类业务逻辑需求来提供接口

    • 通过代理协议的可选实现的方法获取的属性值的属性,需要设置默认值
    • 未提供默认值的且必须使用的属性,需要通过必须实现的方法来获得
    • 自定义布局提供的接口可选

      • 列数
      • 列之间的间距
      • 行之间的间距
      • 内边距
    • 自定义布局提供的接口必选

      • 每个元素的高度,宽度可以通过列数和列间距计算得到

封装步骤


  • 设置代理协议,提供接口

    //声明LYPWaterFlowLayout为一个类
    @class LYPWaterFlowLayout;
    @protocol LYPWaterFlowLayoutDelegate <NSObject>
    //必须实现的方法
    @required
    /**获取瀑布流每个元素的高度*/
    - (CGFloat)waterFlowLayout:(LYPWaterFlowLayout *)waterFlowLayout heightForItemAtIndex:(NSInteger)index itemWith:(CGFloat)itemWith;
    //可选实现的方法
    @optional
    /**获取瀑布流的列数*/
    - (NSInteger)columnCountInWaterFlowLayout:(LYPWaterFlowLayout *)waterFlowLayout;
    /**获取瀑布流列间距*/
    - (CGFloat)columnMarginInWaterFlowLayout:(LYPWaterFlowLayout *)waterFlowLayout;
    /**获取瀑布流的行间距*/
    - (CGFloat)rowMarginInWaterFlowLayout:(LYPWaterFlowLayout *)waterFlowLayout;
    /**获取瀑布流的内边距*/
    - (UIEdgeInsets)edgeInsetsInWaterFlowLayout:(LYPWaterFlowLayout *)waterFlowLayout;
    @end
  • 设置代理属性

    @interface LYPWaterFlowLayout : UICollectionViewLayout
    /**代理*/
    @property (nonatomic, weak) id<LYPWaterFlowLayoutDelegate> delegate;
    @end
  • 设置通过可选代理方法获取属性值的属性的默认值

    /**默认的列数*/
    static const NSInteger LYPDefaultColumnCount = 3;
    /**默认每一列之间的间距*/
    static const CGFloat LYPDefaultColumMargin = 10;
    /**默认每一行之间的间距*/
    static const CGFloat LYPDefaultRowMargin = 10;
    /**默认边缘间距*/
    static const UIEdgeInsets LYPDefaultEdgeInsets = {10, 10, 10, 10};
  • 设置通过可选代理方法获取属性值的属性的访问方式若代理提供属性值,则忽略默认值

    - (NSInteger)columnCount
    {
    //判断代理是否实现了获取列数的可选方法
    if ([self.delegate respondsToSelector:@selector(columnCountInWaterFlowLayout:)])
    {
    //实现,返回通过代理设置的列数
    return [self.delegate columnCountInWaterFlowLayout:self];
    }
    else
    {
    //为实现,返回默认的列数
    return LYPDefaultColumnCount;
    }
    }
    • 注:其他属性值的获取与上述方法几乎完全相同,不再赘述
  • 设置布局

    • 设置需要的成员属性

      /**所有cell的布局属性*/
      @property (nonatomic, strong) NSMutableArray *attrsArray;
      /**所有列的当前高度*/
      @property (nonatomic, strong) NSMutableArray *columnHeights;
    • 通过懒加载的方式初始化成员属性

      /**--attrsArray--懒加载*/
      - (NSMutableArray *)attrsArray
      {
      if (_attrsArray == nil)
      {
      _attrsArray = [NSMutableArray array];
      }
      return _attrsArray;
      }
      /**--columnHeights--懒加载*/
      - (NSMutableArray *)columnHeights
      {
      if (_columnHeights == nil)
      {
      _columnHeights = [NSMutableArray array];
      }
      return _columnHeights;
      }
    • 初始化布局

      - (void)prepareLayout
      {
      [super prepareLayout]; /**清除之前跟布局相关的所有属性,重新设置新的布局*/
      //清除之前计算的所有列的高度
      [self.columnHeights removeAllObjects];
      //设置所有列的初始高度
      for (NSInteger i = 0; i<self.columnCount; i++)
      {
      self.columnHeights[i] = @(self.edgeInsets.top);
      }
      //清除之前所有的布局属性
      [self.attrsArray removeAllObjects]; /**开始创建每一个cell对应的布局属性*/
      NSInteger count = [self.collectionView numberOfItemsInSection:0];
      for (NSInteger i = 0; i<count; i++)
      {
      NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
      //获取indexPath位置cell对应的布局属性
      UICollectionViewLayoutAttributes *attrs = [self layoutAttributesForItemAtIndexPath:indexPath];
      //将indexPath位置的cell的布局属性添加到所有cell的布局属性数组中
      [self.attrsArray addObject:attrs];
      }
      }
    • 返回包含所有cell的布局属性的数组

      - (nullable NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect
      {
      return self.attrsArray;
      }
    • 设置每一个cell的布局属性

      - (nullable UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(nonnull NSIndexPath *)indexPath
      {
      //获取indexPath位置的布局属性
      UICollectionViewLayoutAttributes *attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath]; /**设置cell布局属性的frame*/ /***确定cell的尺寸***/
      //获取collectionView的宽度
      CGFloat collectionViewWidth = self.collectionView.frame.size.width;
      //cell宽度
      CGFloat width = ((collectionViewWidth - self.edgeInsets.left - self.edgeInsets.right - (self.columnCount - 1) * self.columMargin)) / self.columnCount;
      //cell高度
      CGFloat height = [self.delegate waterFlowLayout:self heightForItemAtIndex:indexPath.item itemWith:width]; /***设置cell的位置***/
      NSInteger destColumn = 0;
      CGFloat minColumnHeight = [self.columnHeights[0] doubleValue];
      for (NSInteger i = 1; i<self.columnCount; i++)
      {
      CGFloat columnHeight = [self.columnHeights[i] doubleValue];
      if (minColumnHeight > columnHeight)
      {
      minColumnHeight = columnHeight;
      destColumn = i;
      }
      }
      //计算cell的位置
      CGFloat x = self.edgeInsets.left + destColumn * (width + self.columMargin);
      CGFloat y = minColumnHeight;
      //判断是不是第一行
      if (y != self.edgeInsets.top)
      {
      //若不是第一行,需要加上行间距
      y += self.rowMargin;
      } /**给cell的布局属性的frame赋值*/
      attrs.frame = CGRectMake(x, y, width, height); //更新最短那列的高度
      self.columnHeights[destColumn] = @(CGRectGetMaxY(attrs.frame)); /**返回indexPath位置的cell的布局属性*/
      return attrs;
      }
    • 设置collectionView内容的尺寸

      - (CGSize)collectionViewContentSize
      {
      //获取最高的那一列的高度
      CGFloat maxColumnHeight = [self.columnHeights[0] doubleValue];
      for (NSInteger i = 1; i<self.columnCount; i++)
      {
      CGFloat columnHeight = [self.columnHeights[i] doubleValue];
      if (maxColumnHeight < columnHeight)
      {
      maxColumnHeight = columnHeight;
      }
      }
      //返回collectionView的contentSize,高度为最高的高度加上一个行间距
      return CGSizeMake(0, maxColumnHeight + self.rowMargin);
      }
 
 

OC - 30.如何封装自定义布局的更多相关文章

  1. OC - 31.通过封装的自定义布局快速实现商品展示

    概述 实现效果 设计思路 采用MVC架构,即模型—视图-控制器架构 使用MJExtension框架实现字典转模型 使用MJRefresh框架实现上拉和下拉刷新 上拉刷新,加载新的数据 下拉刷新,加载更 ...

  2. OC - 29.自定义布局实现瀑布流

    概述 瀑布流是电商应用展示商品通常采用的一种方式,如图示例 瀑布流的实现方式,通常有以下几种 通过UITableView实现(不常用) 通过UIScrollView实现(工作量较大) 通过UIColl ...

  3. Swift - 使用网格(UICollectionView)的自定义布局实现复杂页面

    网格UICollectionView除了使用流布局,还可以使用自定义布局.实现自定义布局需要继承UICollectionViewLayout,同时还要重载下面的三个方法: 1 2 3 4 5 6 7 ...

  4. Collection View 自定义布局(custom flow layout)

    Collection view自定义布局 一般我们自定义布局都会新建一个类,继承自UICollectionViewFlowLayout,然后重写几个方法: prepareLayout():当准备开始布 ...

  5. AcitonBar 自定义布局

    Android系统中ActionBar默认的布局不美观且难于控制,通过为ActionBar自定义布局的方式可以灵活控制ActionBar. 自定义Activity主题和ActionBar样式 在新建的 ...

  6. Xamarin自定义布局系列——瀑布流布局

    Xamarin.Forms以Xamarin.Android和Xamarin.iOS等为基础,自己实现了一整套比较完整的UI框架,包含了绝大多数常用的控件,如下图 虽然XF(Xamarin.Forms简 ...

  7. Flutter自定义布局套路

    开始 在Android中我们要实现一个布局需要继承ViewGroup, 重写其中的onLayout和onMeasure方法. 其中onLayout负责给子控件设置布局区域, onMeaseure度量子 ...

  8. ActionBar 自定义布局定义

    ActionBar 自定义布局定义   Android系统中ActionBar默认的布局不美观且难于控制,通过为ActionBar自定义布局的方式可以灵活控制ActionBar. 效果: 工具/原料 ...

  9. 干货之UIButton的title和image自定义布局

    当需要实现一个自定义布局图片和标题的按钮时候,不知道有多少少年直接布局了UIButton,亦或是自定义一个UIView,然后以空白UIButton.UILabel.UIImageVew作为subVie ...

随机推荐

  1. 精确到时分秒的jQuery插件例子

    demo.html: <!DOCTYPE html> <html> <head>  <meta http-equiv="Content-Type&q ...

  2. 【HDOJ】3549 Flow Problem

    网络流基础题目,Edmonds_Karp可解. /* 3549 */ #include <iostream> #include <string> #include <ma ...

  3. BZOJ1610: [Usaco2008 Feb]Line连线游戏

    1610: [Usaco2008 Feb]Line连线游戏 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 1301  Solved: 571[Submit ...

  4. 【转】vc中使用SendMessage正确发送自定义消息的方法--不错

    原文网址:http://zhoumf1214.blog.163.com/blog/static/5241940200910265532959/ 最近在用VC2008做开发,后来由于要用到消息的发送,而 ...

  5. Linux学习笔记3——Linux中常用系统管理命令

    stat 显示指定文件的相关信息,比ls命令显示内容更多 who 显示在线登录用户 hostname 显示主机名称 uname显示系统信息 top 显示当前系统中耗费资源最多的进程 ps 显示瞬间的进 ...

  6. Unity3d 使用DX11的曲面细分

    Unity3d surface Shaderswith DX11 Tessellation Unity3d surface shader 在DX11上的曲面细分 I write this articl ...

  7. HDOJ1021题 Fibonacci Again 应用求模公式

    Problem Description There are another kind of Fibonacci numbers: F(0) = 7, F(1) = 11, F(n) = F(n-1) ...

  8. linux驱动面试题目汇总

    http://blog.csdn.net/blueice8601/article/details/7666427 1.linux驱动分类 2.信号量与自旋锁 3.platform总线设备及总线设备如何 ...

  9. linux创建用户,指定组

    本博客不再更新 该文章新链接移步:http://it.lovepet.vip/archives/7/ 一.创建用户: 1.使用命令 useradd 例:useradd test——创建用户test  ...

  10. Python基础知识---字典

    现在在实习期间,好久没用Python了,今天在做Java项目时用的HashMap让我联想到了Python中的字典,就写一些Python字典的知识吧,复习复习. 字典:  key --> valu ...