概述


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

代理设计模式


  • 代理设计模式的组成

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

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

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

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

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

自定义布局类的封装


  • 业务逻辑

    • 如图

  • 布局每个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. [LeetCode#116]Fraction to Recurring Decimal

    Problem: Given two integers representing the numerator and denominator of a fraction, return the fra ...

  2. Windows作业

    1.什么是Windows作业 Windows作业实际上一个进程组,可以给作业设置权限,一旦进程加入到作业内,进程的权限将会被作业限制. 2.创建一个作业 HANDLE CreateJobObject( ...

  3. vijosP1046 观光旅游(最小环)

    vijosP1046 观光旅游 链接:https://vijos.org/p/1046 [思路] Floyd求解最小环. [代码] #include<iostream> using nam ...

  4. MT9v024总结

    S1: A typical READ or WRITE sequence begins by the master sending a start bit. After thestart bit, t ...

  5. [原]ubuntu下制作openstack-havana源

    ubuntu下可以用apt-mirror下载openstack的源: 1.安装apt-mirror: apt-get install apt-mirror 2.配置/etc/apt/mirror.li ...

  6. 数据结构学习笔记——stack实现(数组篇)

    一 栈:是一种表,限制插入和删除只能在一个位置,也即是表的末端(也是栈的顶)进行. 基本操作:push 和 pop. 二 栈的数组实现: 运用数组来存储元素,和栈操作先关的是theArray(一个数组 ...

  7. java 解析xml文件案例

    package xmlTest; import javax.xml.parsers.*; import org.w3c.dom.*; public class GetXml { public stat ...

  8. intellij安装 配置 创建项目

    使用intellij创建项目的整个过程如下: 首先,点击intllij的.exe文件,如果是第一次安装,选择第二个选项即可 Intellij需要license key,可以使用注册机生成相应的name ...

  9. UVa11925 Generating Premutations

    留坑(p.254) #include<cstdio> #include<cstring> #include<cstdlib> #include<algorit ...

  10. PDOstament对象执行execute()函数,只要是sql语句正确都是返回true

    [PDO对象操作数据库] PDOstament对象执行execute()函数,只要是sql语句正确都是返回true. 问题: 想要PDO对象实现更改一条记录, 并修改是否成功要返回信息给用户. 上我的 ...