IOS封装自定义布局的方法

一、概述
1、对于经常使用的控件或类,通常将其分装为一个单独的类来供外界使用,以此达到事半功倍的效果
2、由于分装的类不依赖于其他的类,所以若要使用该类,可直接将该类拖进项目文件即可
3、在进行分装的时候,通常需要用到代理设计模式
二、代理设计模式
1、代理设计模式的组成
客户类(通常作为代理):通常委托这是角色来完成业务逻辑
真实角色:将客户类的业务逻辑转化为方法列表,即代理协议
代理协议:

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

代理角色

  • 若要作为代理,需要遵守代理协议,并且实现必须实现的代理方法
  • 代理角色可以通过调用代理协议中的方法完成业务逻辑,也可以附加自己的操作

文字描述通常是抽象的,一下通过图示来阐述代理设计模式

三、自定义布局类的封装
1、业务逻辑
如图

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

3、封装思路封装需要根据客户类业务逻辑需求来提供接口
1)、通过代理协议的可选实现的方法获取的属性值的属性,需要设置默认值
2)、未提供默认值的且必须使用的属性,需要通过必须实现的方法来获得
3)、自定义布局提供的接口可选

  • 列数
  • 列之间的间距
  • 行之间的间距
  • 内边距

4)、自定义布局提供的接口必选
每个元素的高度,宽度可以通过列数和列间距计算得到
四、封装步骤
设置代理协议,提供接口

//声明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;
  }
}

注:其他属性值的获取与上述方法几乎完全相同,不再赘述
设置布局
1)、设置需要的成员属性

/**所有cell的布局属性*/
@property (nonatomic, strong) NSMutableArray *attrsArray;
/**所有列的当前高度*/
@property (nonatomic, strong) NSMutableArray *columnHeights;

2)、通过懒加载的方式初始化成员属性

/**--attrsArray--懒加载*/
- (NSMutableArray *)attrsArray
{
  if (_attrsArray == nil)
  {
    _attrsArray = [NSMutableArray array];
  }
  return _attrsArray;
}
/**--columnHeights--懒加载*/
- (NSMutableArray *)columnHeights
{
  if (_columnHeights == nil)
  {
    _columnHeights = [NSMutableArray array];
  }
  return _columnHeights;
}

3)、初始化布局

- (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];
  }
}

4)、返回包含所有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;
}

5)、设置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);
}

以上就是本文的全部内容,希望对大家的学习有所帮助。

(0)

相关推荐

  • 深入解析iOS应用开发中九宫格视图布局的相关计算方法

    来看一个简单的例子: 复制代码 代码如下: /*  * 总列数  */ NSUInteger totalloc = 3; /*  * View的宽高  */ CGFloat shopW = 80; CGFloat shopH = 100; /*  * 每个View之间的间隔  */ CGFloat margin = (self.view.frame.size.width - totalloc * shopW) / (totalloc + 1); /*  * View的总个数  */ NSUInt

  • iOS模仿电子书首页实现书架布局样式

    本文实现了类似电子书首页,用来展示图书或小说的布局页面,书架列表[iPhone6模拟器],屏幕尺寸还没进行适配,只是做个简单的demo[纯代码实现方式] 实现采用的是UICollectionView和UICollectionViewFlowLayout.关于UICollectionView的详细讲解请参考 一.实现layout的DecorationView // // FWBookShelfDecarationViewCollectionReusableView.h // FWPersonalA

  • IOS实现自定义布局瀑布流

    瀑布流是电商应用展示商品通常采用的一种方式,如图示例 瀑布流的实现方式,通常有以下几种 通过UITableView实现(不常用) 通过UIScrollView实现(工作量较大) 通过UICollectionView实现(通常采用的方式) 一.UICollectionView基础 1.UICollectionView与UITableView有很多相似的地方,如 都通过数据源提供数据 都通过代理执行相关的事件 都可以自定义cell,且涉及到cell的重用 都继承自UIScrollView,具有滚动效

  • 详解iOS中UIView的layoutSubviews子视图布局方法使用

    概念 在UIView里面有一个方法layoutSubviews: 复制代码 代码如下: - (void)layoutSubviews;    // override point. called by layoutIfNeeded automatically. As of iOS 6.0, when constraints-based layout is used the base implementation applies the constraints-based layout, other

  • iOS开发之手动布局子视图

    手动布局子视图: 下面先看下效果图,我们今天要实现的效果: 这里我们默认用storyboard启动: 首先我们要在白色的屏幕上面创建一个父视图SuperView(蓝色的背景),在父视图里面创建四个小视图(橘黄色的背景) 下面看代码, 在SuperView.h文件里面: #import <UIKit/UIKit.h> @interface SuperView : UIView{ UIView * _view01; UIView * _view02; UIView * _view03; UIVie

  • 详解iOS应用使用Storyboard布局时的IBOutlet与IBAction

    在图形界面编程时,解决的第一问题就是如何将静态界面与代码关联起来,或者说是代码如何与界面上的对象 通信, 代码如何操作界面上的对象.在iPhone平台上,引入了IBOutlet与IBAction.通过在变量前增加IBOutlet 来说明该变量将与界面上的某个UI对象对应,在方法前增加IBAction来说明该方法将与界面上的事件对应. 下面通过一个连接网络服务器(NetworkConnection)的例子来说明IBOutlet与IBAction. 界面上有host 与 port 的Text Fie

  • iOS App开发中Masonry布局框架的基本用法解析

    Masonry是一个轻量级的布局框架,拥有自己的描述语法,采用更优雅的链式语法封装自动布局,简洁明了并具有高可读性,而且同时支持 iOS 和 Max OS X.Masonry是一个用代码写iOS或OS界面的库,可以代替Auto layout.Masonry的github地址:https://github.com/SnapKit/Masonry Masonry使用讲解: mas_makeConstraints 是给view添加约束,约束有几种,分别是边距,宽,高,左上右下距离,基准线.添加过约束后

  • iOS组件封装与自动布局自定义表情键盘

    下面的东西是编写自定义的表情键盘,话不多说,开门见山吧!下面主要用到的知识有MVC, iOS开发中的自动布局,自定义组件的封装与使用,Block回调,CoreData的使用.有的小伙伴可能会问写一个自定义表情键盘肿么这么麻烦?下面 将会介绍我们如何用上面提到的东西来定义我们的表情键盘的.下面的内容会比较多,这篇文章还是比较有料的. 还是那句话写技术博客是少不了代码的,下面会结合代码来回顾一下iOS的知识,本篇博文中用到的知识点在前面的博客中都能找到相应的内容,本篇 算是一个小小的功能整合.先来张

  • iOS App开发中扩展RCLabel组件进行基于HTML的文本布局

    iOS系统是一个十分注重用户体验的系统,在iOS系统中,用户交互的方案也十分多,然而要在label中的某部分字体中添加交互行为确实不容易的,如果使用其他类似Button的控件来模拟,文字的排版又将是一个解决十分困难的问题.这个问题的由来是项目中的一个界面中有一些广告位标签,而这些广告位的标签却是嵌在文本中的,当用户点击文字标签的位置时,会跳转到响应的广告页. CoreText框架和一些第三方库可以解决这个问题,但直接使用CoreText十分复杂,第三方库多注重于富文本的排版,对类似文字超链接的支

  • IOS封装自定义布局的方法

    一.概述 1.对于经常使用的控件或类,通常将其分装为一个单独的类来供外界使用,以此达到事半功倍的效果 2.由于分装的类不依赖于其他的类,所以若要使用该类,可直接将该类拖进项目文件即可 3.在进行分装的时候,通常需要用到代理设计模式 二.代理设计模式 1.代理设计模式的组成 客户类(通常作为代理):通常委托这是角色来完成业务逻辑 真实角色:将客户类的业务逻辑转化为方法列表,即代理协议 代理协议: 定义了需要实现的业务逻辑 定义了一组方法列表,包括必须实现的方法或选择实现的方法 代理协议是代理对象所

  • Android编程实现Toast自定义布局简单示例

    本文实例讲述了Android编程实现Toast自定义布局的方法.分享给大家供大家参考,具体如下: 不知道各位客官是不是觉得系统的toast的信息很难看呢,默认的但黑色背景,毫无色彩. 那么接下来我就教大家用最简单的方式自定义toast布局吧. 首先加载一个自定义的布局 LayoutInflater inflater = context.getLayoutInflater(); View view=inflater.inflate(R.layout.toast_info, null); 然后找到里

  • IOS开发自定义view方法规范示例

    目录 前言 一.关于自定义View的初始化方法 二.关于addSubview 三.关于layoutSubviews 四.关于frame与bounds 总结 前言 对于接触业务开发的童鞋,自定义View的开发是进行最频繁的工作了.但发现一些童鞋还是没有以一个好的规范甚至以一种错误的方式来搭建UI控件.由此,本文将以以下目录来进行讲叙,详细描述关于自定义View的一些书写注意事项. 关于自定义View的初始化方法 关于addSubview 关于layoutSubviews 关于frame与bound

  • IOS中自定义类中限制使用原生实例化方法

    IOS中自定义类中限制使用原生实例化方法 在自定义的类中,除了有系统自带的实例化方法外,还可能会有开发者自定义的实例化方法.当不想使用系统自定义方法时,而仅使用自定义的实例化方法时,可以这样做下限制. 如下示例所示: #import <UIKit/UIKit.h> @interface MYView : UIView // 限制使用系统方法进行实例化 // 方法1 - (instancetype)init UNAVAILABLE_ATTRIBUTE; // 方法2 - (instancetyp

  • Android布局——Preference自定义layout的方法

    导语:PreferenceActivity是一个方便设置管理的界面,但是对于界面显示来说比较单调,所以自定义布局就很有必要了.本文举例说明在Preference中自定义layout的方法.笔者是为了在设置中插入@有米v4广告条才研究了一晚上的. 正文:首先PreferenceScreen是一个xml文件于res/xml目录下,不属于layout文件.要插入layout,有两种方法. 1.使用Preference的android:@layout属性 1)xml文件中preference的添加 复制

  • Android简单实现自定义流式布局的方法

    本文实例讲述了Android简单实现自定义流式布局的方法.分享给大家供大家参考,具体如下: 首先来看一下 手淘HD - 商品详情 - 选择商品属性 页面的UI 商品有很多尺码,而且展现每个尺码所需要的View的大小也不同(主要是宽度),所以在从服务器端拉到数据之前,展现所有尺码所需要的行数和每一行的个数都无法确定,因此不能直接使用GridView或ListView. 如果使用LinearLayout呢? 一个LinearLayout只能显示一行,如果要展示多行,则每一行都要new一个Linear

  • Android不使用自定义布局情况下实现自定义通知栏图标的方法

    本文实例讲述了Android不使用自定义布局情况下实现自定义通知栏图标的方法.分享给大家供大家参考,具体如下: 自定义通知栏图标?不是很简单么.自定义布局都不在话下! 是的,有xml布局文件当然一切都很简单,如果不给你布局文件用呢? 听我慢慢道来! 首先怎么创建一个通知呢? 1.new 一个 复制代码 代码如下: Notification n = new Notification(android.R.drawable.ic_menu_share, null, System.currentTime

  • ios的collection控件的自定义布局实现与设计

    collection控件用来实现界面的各种自定义布局,最常用其作为横向.竖向的布局控件.很早之前,系统对于collection的支持并不是很好.所以自己实现了支持自定义布局.自定义cell的collection控件.自定义的collection可以满足所有的产品特殊需求及动态效果,例如在某些特殊情况下可能需要除选中cell之外的其它cell执行布局动画等.在collection的基础之上,我又实现了支持cell拖动.拖离窗体的tabview控件.本文主要介绍自定义collection的设计与实现

  • iOS 控制器自定义动画跳转方法(模态跳转)

    参考资料: Apple 开发文档 Customizing the Transition Animations WWDC 2013 Custom Transitions Using View Controllers 图例: 跳转的动画有很多,全部可以自定义 创建自定义跳转必须遵循的三个步骤: 1.创建一个类,并实现了 UIViewControllerAnimatedTransitioning 协议 2.创建一个类作为 UIViewControllerTransitioningDelegate 过渡

随机推荐