一、概述
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); }
以上就是本文的全部内容,希望对大家的学习有所帮助。
本文向大家介绍IOS实现自定义布局瀑布流,包括了IOS实现自定义布局瀑布流的使用技巧和注意事项,需要的朋友参考一下 瀑布流是电商应用展示商品通常采用的一种方式,如图示例 瀑布流的实现方式,通常有以下几种 通过UITableView实现(不常用) 通过UIScrollView实现(工作量较大) 通过UICollectionView实现(通常采用的方式) 一、UICollectionView基础 1、
有没有办法为不同的日志级别打印不同的布局?例如: 记录器。警告(“消息”);打印如下内容:2016-06-20 13:34:41245 INFO(main:)Message and for logger。信息(“消息2”);仅打印:消息2 有可能做到吗?定义一个布局以警告其他布局以获取信息 log4j.properties
我正在使用PreferenceActivity设置我的应用程序。我想添加一个新的首选项,允许用户选择一个图标。对于这个任务,我想使用ListPreference,但我也想在列表中显示图标。 我尝试自定义List首选项以使用自定义布局,但问题是一旦我这样做了,列表项就不可单击(它确实显示了我的自定义布局并使用当前选择的默认值)。 我在不同的模拟器版本和银河S2上测试了它。当按下项目时,我可以看到一些
但是,在Log4JV2中,PatternLayout类被设置为“final”,整个体系结构也被更改。似乎不再有一种简单的方法来拦截/覆盖对PatternLayout对象的调用。我查看了Apache文档,但没有太多信息。 我检查了这个问题和这个问题,但都没有太多的帮助。 我意识到这是一个非常“一般”的问题,但是有没有人知道在Log4j V2中实现这一点的简单方法,或者对此有什么建议?
第一次使用JavaFX,所以我对/方法有一个问题。我在做一个弹跳球项目。首先,我使用对象创建球,但由于需要传递更多参数,我决定构建自己的球类()。这是我以前使用时使用的代码: 当将和和放入中时,就会使“球”弹出墙壁。现在,我正试图弄清楚如何使用ball来实现这一点,因为在使用我自己的对象时,无法解析、和方法。如何处理这个/构建自己的等?
问题内容: 根据AngularJS的教程,控制器功能只是位于全局范围内。 http://docs.angularjs.org/tutorial/step_04 控制器功能本身是自动解析为封装范围还是驻留在全局范围内?我知道他们已经传递了对自己的$ scope的引用,但是看来函数本身只是位于全局范围内。显然,这可能会带来很多问题,而且我已经通过经验和教育学到了封装的方法。此外,如果它们确实存在于全球
问题内容: 我正在尝试使用自己的布局创建DialogFragment。 我见过几种不同的方法。有时,布局是在OnCreateDialog中这样设置的:(我使用的是Mono,但是我已经习惯了Java) 第一种方法对我有用…直到我想使用, 所以在经过一段时间的搜索之后,我尝试了第二种方法,该方法涉及覆盖 因此,我注释掉了设置布局的两行,然后添加了以下内容: 这给了我一个可爱的错误: 我很沮丧 问题答案