ios开发——实用技术篇&自定义Collection与使用

自定义Collection与使用
实现步骤
一、新建两个类
1.继承自UIScrollView的子类,比如iCocosWaterflowView
* 瀑布流显示控件,用来显示所有的瀑布流数据

2.继承自UIView的子类,比如iCocosWaterflowViewCell
* 代表着瀑布流数据中的一个单元(一个格子)

3.总结
iCocosWaterflowView和iCocosWaterflowViewCell的关系实际上类似于
UITableView和UITableViewCell的关系

二、设计iCocosWaterflowView的接口
1.模仿UITableView的接口来设计
* 设计一套数据源和代理方法

三、iCocosWaterflowView的实现
1.reloadData
* 作用:刷新数据
* 步骤:
1> 清空以前残余的数据
2> 计算所有新数据对应的frame

2.layoutSubviews
* 作用:显示和移除子控件
* 步骤:
1> 检测每一个数据对应的frame在不在屏幕上(用户能不能看见)
2> 如果这个数据对应的frame在屏幕上:向数据源索要这个数据对应的cell,添加这个cell到iCocosWaterflowView中显示
3> 如果这个数据对应的frame不在屏幕上:从iCocosWaterflowView中移除这个数据对应的cell,并将这个cell添加到缓存池中
 
 
View.h文件
 1 #import <UIKit/UIKit.h>

 2 

 3 typedef enum {

 4     iCocosWaterflowViewMarginTypeTop,

 5     iCocosWaterflowViewMarginTypeBottom,

 6     iCocosWaterflowViewMarginTypeLeft,

 7     iCocosWaterflowViewMarginTypeRight,

 8     iCocosWaterflowViewMarginTypeColumn, // 每一列

 9     iCocosWaterflowViewMarginTypeRow, // 每一行

10 } iCocosWaterflowViewMarginType;

11 

12 @class iCocosWaterflowView, iCocosWaterflowViewCell;

13 

14 /**

15  *  数据源方法

16  */

17 @protocol iCocosWaterflowViewDataSource <NSObject>

18 @required

19 /**

20  *  一共有多少个数据

21  */

22 - (NSUInteger)numberOfCellsInWaterflowView:(iCocosWaterflowView *)waterflowView;

23 /**

24  *  返回index位置对应的cell

25  */

26 - (iCocosWaterflowViewCell *)waterflowView:(iCocosWaterflowView *)waterflowView cellAtIndex:(NSUInteger)index;

27 

28 @optional

29 /**

30  *  一共有多少列

31  */

32 - (NSUInteger)numberOfColumnsInWaterflowView:(iCocosWaterflowView *)waterflowView;

33 @end

34 

35 /**

36  *  代理方法

37  */

38 @protocol iCocosWaterflowViewDelegate <UIScrollViewDelegate>

39 @optional

40 /**

41  *  第index位置cell对应的高度

42  */

43 - (CGFloat)waterflowView:(iCocosWaterflowView *)waterflowView heightAtIndex:(NSUInteger)index;

44 /**

45  *  选中第index位置的cell

46  */

47 - (void)waterflowView:(iCocosWaterflowView *)waterflowView didSelectAtIndex:(NSUInteger)index;

48 /**

49  *  返回间距

50  */

51 - (CGFloat)waterflowView:(iCocosWaterflowView *)waterflowView marginForType:(iCocosWaterflowViewMarginType)type;

52 @end

53 

54 /**

55  *  瀑布流控件

56  */

57 @interface iCocosWaterflowView : UIScrollView

58 /**

59  *  数据源

60  */

61 @property (nonatomic, weak) id<iCocosWaterflowViewDataSource> dataSource;

62 /**

63  *  代理

64  */

65 @property (nonatomic, weak) id<iCocosWaterflowViewDelegate> delegate;

66 

67 /**

68  *  刷新数据(只要调用这个方法,会重新向数据源和代理发送请求,请求数据)

69  */

70 - (void)reloadData;

71 

72 /**

73  *  cell的宽度

74  */

75 - (CGFloat)cellWidth;

76 

77 /**

78  *  根据标识去缓存池查找可循环利用的cell

79  */

80 - (id)dequeueReusableCellWithIdentifier:(NSString *)identifier;

81 @end
View Code

 

View.m文件

  1 #import "iCocosWaterflowView.h"

  2 #import "iCocosWaterflowViewCell.h"

  3 

  4 #define iCocosWaterflowViewDefaultCellH 70

  5 #define iCocosWaterflowViewDefaultMargin 8

  6 #define iCocosWaterflowViewDefaultNumberOfColumns 3

  7 

  8 @interface iCocosWaterflowView()

  9 /**

 10  *  所有cell的frame数据

 11  */

 12 @property (nonatomic, strong) NSMutableArray *cellFrames;

 13 /**

 14  *  正在展示的cell

 15  */

 16 @property (nonatomic, strong) NSMutableDictionary *displayingCells;

 17 /**

 18  *  缓存池(用Set,存放离开屏幕的cell)

 19  */

 20 @property (nonatomic, strong) NSMutableSet *reusableCells;

 21 @end

 22 

 23 @implementation iCocosWaterflowView

 24 

 25 #pragma mark - 初始化

 26 - (NSMutableArray *)cellFrames

 27 {

 28     if (_cellFrames == nil) {

 29         self.cellFrames = [NSMutableArray array];

 30     }

 31     return _cellFrames;

 32 }

 33 

 34 - (NSMutableDictionary *)displayingCells

 35 {

 36     if (_displayingCells == nil) {

 37         self.displayingCells = [NSMutableDictionary dictionary];

 38     }

 39     return _displayingCells;

 40 }

 41 

 42 - (NSMutableSet *)reusableCells

 43 {

 44     if (_reusableCells == nil) {

 45         self.reusableCells = [NSMutableSet set];

 46     }

 47     return _reusableCells;

 48 }

 49 

 50 - (id)initWithFrame:(CGRect)frame

 51 {

 52     self = [super initWithFrame:frame];

 53     if (self) {

 54         

 55     }

 56     return self;

 57 }

 58 

 59 - (void)willMoveToSuperview:(UIView *)newSuperview

 60 {

 61     [self reloadData];

 62 }

 63 

 64 #pragma mark - 公共接口

 65 /**

 66  *  cell的宽度

 67  */

 68 - (CGFloat)cellWidth

 69 {

 70     // 总列数

 71     int numberOfColumns = [self numberOfColumns];

 72     CGFloat leftM = [self marginForType:iCocosWaterflowViewMarginTypeLeft];

 73     CGFloat rightM = [self marginForType:iCocosWaterflowViewMarginTypeRight];

 74     CGFloat columnM = [self marginForType:iCocosWaterflowViewMarginTypeColumn];

 75     return (self.bounds.size.width - leftM - rightM - (numberOfColumns - 1) * columnM) / numberOfColumns;

 76 }

 77 

 78 /**

 79  *  刷新数据

 80  */

 81 - (void)reloadData

 82 {

 83     // 清空之前的所有数据

 84     // 移除正在正在显示cell

 85     [self.displayingCells.allValues makeObjectsPerformSelector:@selector(removeFromSuperview)];

 86     [self.displayingCells removeAllObjects];

 87     [self.cellFrames removeAllObjects];

 88     [self.reusableCells removeAllObjects];

 89     

 90     // cell的总数

 91     int numberOfCells = [self.dataSource numberOfCellsInWaterflowView:self];

 92     

 93     // 总列数

 94     int numberOfColumns = [self numberOfColumns];

 95     

 96     // 间距

 97     CGFloat topM = [self marginForType:iCocosWaterflowViewMarginTypeTop];

 98     CGFloat bottomM = [self marginForType:iCocosWaterflowViewMarginTypeBottom];

 99     CGFloat leftM = [self marginForType:iCocosWaterflowViewMarginTypeLeft];

100     CGFloat columnM = [self marginForType:iCocosWaterflowViewMarginTypeColumn];

101     CGFloat rowM = [self marginForType:iCocosWaterflowViewMarginTypeRow];

102     

103     // cell的宽度

104     CGFloat cellW = [self cellWidth];

105     

106     // 用一个C语言数组存放所有列的最大Y值

107     CGFloat maxYOfColumns[numberOfColumns];

108     for (int i = 0; i<numberOfColumns; i++) {

109         maxYOfColumns[i] = 0.0;

110     }

111     

112     // 计算所有cell的frame

113     for (int i = 0; i<numberOfCells; i++) {

114         // cell处在第几列(最短的一列)

115         NSUInteger cellColumn = 0;

116         // cell所处那列的最大Y值(最短那一列的最大Y值)

117         CGFloat maxYOfCellColumn = maxYOfColumns[cellColumn];

118         // 求出最短的一列

119         for (int j = 1; j<numberOfColumns; j++) {

120             if (maxYOfColumns[j] < maxYOfCellColumn) {

121                 cellColumn = j;

122                 maxYOfCellColumn = maxYOfColumns[j];

123             }

124         }

125         

126         // 询问代理i位置的高度

127         CGFloat cellH = [self heightAtIndex:i];

128         

129         // cell的位置

130         CGFloat cellX = leftM + cellColumn * (cellW + columnM);

131         CGFloat cellY = 0;

132         if (maxYOfCellColumn == 0.0) { // 首行

133             cellY = topM;

134         } else {

135             cellY = maxYOfCellColumn + rowM;

136         }

137         

138         // 添加frame到数组中

139         CGRect cellFrame = CGRectMake(cellX, cellY, cellW, cellH);

140         [self.cellFrames addObject:[NSValue valueWithCGRect:cellFrame]];

141         

142         // 更新最短那一列的最大Y值

143         maxYOfColumns[cellColumn] = CGRectGetMaxY(cellFrame);

144     }

145     

146     // 设置contentSize

147     CGFloat contentH = maxYOfColumns[0];

148     for (int j = 1; j<numberOfColumns; j++) {

149         if (maxYOfColumns[j] > contentH) {

150             contentH = maxYOfColumns[j];

151         }

152     }

153     contentH += bottomM;

154     self.contentSize = CGSizeMake(0, contentH);

155 }

156 

157 /**

158  *  当UIScrollView滚动的时候也会调用这个方法

159  */

160 - (void)layoutSubviews

161 {

162     [super layoutSubviews];

163     

164     // 向数据源索要对应位置的cell

165     NSUInteger numberOfCells = self.cellFrames.count;

166     for (int i = 0; i<numberOfCells; i++) {

167         // 取出i位置的frame

168         CGRect cellFrame = [self.cellFrames[i] CGRectValue];

169         

170         // 优先从字典中取出i位置的cell

171         iCocosWaterflowViewCell *cell = self.displayingCells[@(i)];

172         

173         // 判断i位置对应的frame在不在屏幕上(能否看见)

174         if ([self isInScreen:cellFrame]) { // 在屏幕上

175             if (cell == nil) {

176                 cell = [self.dataSource waterflowView:self cellAtIndex:i];

177                 cell.frame = cellFrame;

178                 [self addSubview:cell];

179                 

180                 // 存放到字典中

181                 self.displayingCells[@(i)] = cell;

182             }

183         } else {  // 不在屏幕上

184             if (cell) {

185                 // 从scrollView和字典中移除

186                 [cell removeFromSuperview];

187                 [self.displayingCells removeObjectForKey:@(i)];

188                 

189                 // 存放进缓存池

190                 [self.reusableCells addObject:cell];

191             }

192         }

193     }

194 }

195 

196 - (id)dequeueReusableCellWithIdentifier:(NSString *)identifier

197 {

198     __block iCocosWaterflowViewCell *reusableCell = nil;

199     

200     [self.reusableCells enumerateObjectsUsingBlock:^(iCocosWaterflowViewCell *cell, BOOL *stop) {

201         if ([cell.identifier isEqualToString:identifier]) {

202             reusableCell = cell;

203             *stop = YES;

204         }

205     }];

206     

207     if (reusableCell) { // 从缓存池中移除

208         [self.reusableCells removeObject:reusableCell];

209     }

210     return reusableCell;

211 }

212 

213 #pragma mark - 私有方法

214 /**

215  *  判断一个frame有无显示在屏幕上

216  */

217 - (BOOL)isInScreen:(CGRect)frame

218 {

219     return (CGRectGetMaxY(frame) > self.contentOffset.y) &&

220     (CGRectGetMinY(frame) < self.contentOffset.y + self.bounds.size.height);

221 }

222 

223 /**

224  *  间距

225  */

226 - (CGFloat)marginForType:(iCocosWaterflowViewMarginType)type

227 {

228     if ([self.delegate respondsToSelector:@selector(waterflowView:marginForType:)]) {

229         return [self.delegate waterflowView:self marginForType:type];

230     } else {

231         return iCocosWaterflowViewDefaultMargin;

232     }

233 }

234 /**

235  *  总列数

236  */

237 - (NSUInteger)numberOfColumns

238 {

239     if ([self.dataSource respondsToSelector:@selector(numberOfColumnsInWaterflowView:)]) {

240         return [self.dataSource numberOfColumnsInWaterflowView:self];

241     } else {

242         return iCocosWaterflowViewDefaultNumberOfColumns;

243     }

244 }

245 /**

246  *  index位置对应的高度

247  */

248 - (CGFloat)heightAtIndex:(NSUInteger)index

249 {

250     if ([self.delegate respondsToSelector:@selector(waterflowView:heightAtIndex:)]) {

251         return [self.delegate waterflowView:self heightAtIndex:index];

252     } else {

253         return iCocosWaterflowViewDefaultCellH;

254     }

255 }

256 

257 #pragma mark - 事件处理

258 - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event

259 {

260     if (![self.delegate respondsToSelector:@selector(waterflowView:didSelectAtIndex:)]) return;

261     

262     // 获得触摸点

263     UITouch *touch = [touches anyObject];

264     //    CGPoint point = [touch locationInView:touch.view];

265     CGPoint point = [touch locationInView:self];

266     

267     __block NSNumber *selectIndex = nil;

268     [self.displayingCells enumerateKeysAndObjectsUsingBlock:^(id key, iCocosWaterflowViewCell *cell, BOOL *stop) {

269         if (CGRectContainsPoint(cell.frame, point)) {

270             selectIndex = key;

271             *stop = YES;

272         }

273     }];

274     

275     if (selectIndex) {

276         [self.delegate waterflowView:self didSelectAtIndex:selectIndex.unsignedIntegerValue];

277     }

278 }
View Code

 

Cell.h文件

1 #import <UIKit/UIKit.h>

2 

3 @interface iCocosWaterflowViewCell : UIView

4 @property (nonatomic, copy) NSString *identifier;

5 @end
View Code

使用方法:

1:新建一个Cell模型

1 #import <Foundation/Foundation.h>

2 

3 @interface iCocosShop : NSObject

4 @property (nonatomic, assign) CGFloat w;

5 @property (nonatomic, assign) CGFloat h;

6 @property (nonatomic, copy) NSString *img;

7 @property (nonatomic, copy) NSString *price;

8 @end
View Code

2:定义一个集成自上面的Cell的Cell

1 #import "iCocosWaterflowViewCell.h"

2 @class iCocosWaterflowView, iCocosShop;

3 

4 @interface iCocosShopCell : iCocosWaterflowViewCell

5 + (instancetype)cellWithWaterflowView:(iCocosWaterflowView *)waterflowView;

6 

7 @property (nonatomic, strong) iCocosShop *shop;

8 @end
View Code

实现这个Cell:

 1 #import "iCocosShopCell.h"

 2 #import "iCocosWaterflowView.h"

 3 #import "UIImageView+WebCache.h"

 4 #import "iCocosShop.h"

 5 

 6 @interface iCocosShopCell()

 7 @property (weak, nonatomic) UIImageView *imageView;

 8 @property (weak, nonatomic) UILabel *priceLabel;

 9 @end

10 

11 @implementation iCocosShopCell

12 

13 

14 + (instancetype)cellWithWaterflowView:(iCocosWaterflowView *)waterflowView

15 {

16     static NSString *ID = @"SHOP";

17     iCocosShopCell *cell = [waterflowView dequeueReusableCellWithIdentifier:ID];

18     if (cell == nil) {

19         cell = [[iCocosShopCell alloc] init];

20         cell.identifier = ID;

21     }

22     return cell;

23 }

24 

25 - (id)initWithFrame:(CGRect)frame

26 {

27     self = [super initWithFrame:frame];

28     if (self) {

29         

30         UIImageView *imageView = [[UIImageView alloc] init];

31         [self addSubview:imageView];

32         self.imageView = imageView;

33         

34         UILabel *priceLabel = [[UILabel alloc] init];

35         priceLabel.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.3];

36         priceLabel.textAlignment = NSTextAlignmentCenter;

37         priceLabel.textColor = [UIColor whiteColor];

38         [self addSubview:priceLabel];

39         self.priceLabel = priceLabel;

40     }

41     return self;

42 }

43 

44 - (void)setShop:(iCocosShop *)shop

45 {

46     _shop = shop;

47     

48     self.priceLabel.text = shop.price;

49     [self.imageView sd_setImageWithURL:[NSURL URLWithString:shop.img] placeholderImage:[UIImage imageNamed:@"loading"]];

50 }

51 

52 - (void)layoutSubviews

53 {

54     [super layoutSubviews];

55     

56     self.imageView.frame = self.bounds;

57     

58     CGFloat priceX = 0;

59     CGFloat priceH = 25;

60     CGFloat priceY = self.bounds.size.height - priceH;

61     CGFloat priceW = self.bounds.size.width;

62     self.priceLabel.frame = CGRectMake(priceX, priceY, priceW, priceH);

63 }
View Code

3:在控制器中直接使用:

  1 #import "iCocosShopsViewController.h"

  2 #import "iCocosShopCell.h"

  3 #import "iCocosWaterflowView.h"

  4 #import "iCocosShop.h"

  5 #import "MJExtension.h"

  6 #import "MJRefresh.h"

  7 

  8 @interface iCocosShopsViewController ()<iCocosWaterflowViewDataSource, iCocosWaterflowViewDelegate>

  9 @property (nonatomic, strong) NSMutableArray *shops;

 10 @property (nonatomic, weak) iCocosWaterflowView *waterflowView;

 11 @end

 12 

 13 @implementation iCocosShopsViewController

 14 

 15 - (NSMutableArray *)shops

 16 {

 17     if (_shops == nil) {

 18         self.shops = [NSMutableArray array];

 19     }

 20     return _shops;

 21 }

 22 

 23 - (void)viewDidLoad

 24 {

 25     [super viewDidLoad];

 26     

 27     // 0.初始化数据

 28     NSArray *newShops = [iCocosShop objectArrayWithFilename:@"2.plist"];

 29     [self.shops addObjectsFromArray:newShops];

 30     

 31     // 1.瀑布流控件

 32     iCocosWaterflowView *waterflowView = [[iCocosWaterflowView alloc] init];

 33     waterflowView.backgroundColor = [UIColor redColor];

 34     // 跟随着父控件的尺寸而自动伸缩

 35     waterflowView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;

 36     waterflowView.frame = self.view.bounds;

 37     waterflowView.dataSource = self;

 38     waterflowView.delegate = self;

 39     [self.view addSubview:waterflowView];

 40     self.waterflowView = waterflowView;

 41     

 42     // 2.继承刷新控件

 43 //    [waterflowView addFooterWithCallback:^{

 44 //        NSLog(@"进入上拉加载状态");

 45 //    }];

 46     

 47 //    [waterflowView addHeaderWithCallback:^{

 48 //        NSLog(@"进入下拉加载状态");

 49     //    }];

 50     

 51     [waterflowView addHeaderWithTarget:self action:@selector(loadNewShops)];

 52     [waterflowView addFooterWithTarget:self action:@selector(loadMoreShops)];

 53 }

 54 

 55 - (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation

 56 {

 57 //    NSLog(@"屏幕旋转完毕");

 58     [self.waterflowView reloadData];

 59 }

 60 

 61 - (void)loadNewShops

 62 {

 63     static dispatch_once_t onceToken;

 64     dispatch_once(&onceToken, ^{

 65         // 加载1.plist

 66         NSArray *newShops = [iCocosShop objectArrayWithFilename:@"1.plist"];

 67         [self.shops insertObjects:newShops atIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, newShops.count)]];

 68     });

 69     

 70     dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

 71         // 刷新瀑布流控件

 72         [self.waterflowView reloadData];

 73         

 74         // 停止刷新

 75         [self.waterflowView headerEndRefreshing];

 76     });

 77 }

 78 

 79 - (void)loadMoreShops

 80 {

 81     static dispatch_once_t onceToken;

 82     dispatch_once(&onceToken, ^{

 83         // 加载3.plist

 84         NSArray *newShops = [iCocosShop objectArrayWithFilename:@"3.plist"];

 85         [self.shops addObjectsFromArray:newShops];

 86     });

 87     

 88     dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

 89         

 90         // 刷新瀑布流控件

 91         [self.waterflowView reloadData];

 92         

 93         // 停止刷新

 94         [self.waterflowView footerEndRefreshing];

 95     });

 96 }

 97 

 98 #pragma mark - 数据源方法

 99 - (NSUInteger)numberOfCellsInWaterflowView:(iCocosWaterflowView *)waterflowView

100 {

101     return self.shops.count;

102 }

103 

104 - (iCocosWaterflowViewCell *)waterflowView:(iCocosWaterflowView *)waterflowView cellAtIndex:(NSUInteger)index

105 {

106     iCocosShopCell *cell = [iCocosShopCell cellWithWaterflowView:waterflowView];

107     

108     cell.shop = self.shops[index];

109     

110     return cell;

111 }

112 

113 - (NSUInteger)numberOfColumnsInWaterflowView:(iCocosWaterflowView *)waterflowView

114 {

115     if (UIInterfaceOrientationIsPortrait(self.interfaceOrientation)) {

116         // 竖屏

117         return 3;

118     } else {

119         return 5;

120     }

121 }

122 

123 #pragma mark - 代理方法

124 - (CGFloat)waterflowView:(iCocosWaterflowView *)waterflowView heightAtIndex:(NSUInteger)index

125 {

126     iCocosShop *shop = self.shops[index];

127     // 根据cell的宽度 和 图片的宽高比 算出 cell的高度

128     return waterflowView.cellWidth * shop.h / shop.w;

129 }
View Code

注:这里还需要引入第三方库和相应的工具类

 

你可能感兴趣的:(Collection)