iOS开发UI篇—自定义瀑布流控件(蘑菇街数据刷新操作)
一、简单说明
使用数据刷新框架:
该框架提供了两种刷新的方法,一个是使用block回调(存在循环引用问题,_ _weak),一个是使用调用。
问题:在进行下拉刷新之前,应该要清空之前的所有数据(在刷新数据这个方法中)。
移除正在显示的cell:
(1)把字典中的所有的值,都从屏幕上移除
(2)清除字典中的所有元素
(3)清除cell的frame,每个位置的cell的frame都要重新计算
(4)清除可复用的缓存池。
该部分的代码如下:
1 // 2 // YYWaterflowView.m 3 // 06-瀑布流 4 // 5 // Created by apple on 14-7-29. 6 // Copyright (c) 2014年 wendingding. All rights reserved. 7 // 8 9 #import "YYWaterflowView.h" 10 #import "YYWaterflowViewCell.h" 11 #define YYWaterflowViewDefaultNumberOfClunms 3 12 #define YYWaterflowViewDefaultCellH 100 13 #define YYWaterflowViewDefaultMargin 10 14 15 @interface YYWaterflowView() 16 /** 17 * 所有cell的frame数据 18 */ 19 @property(nonatomic,strong)NSMutableArray *cellFrames; 20 /** 21 * 正在展示的cell 22 */ 23 @property(nonatomic,strong)NSMutableDictionary *displayingCells; 24 /** 25 * 缓存池(使用SET) 26 */ 27 @property(nonatomic,strong)NSMutableSet *reusableCells; 28 @end 29 30 @implementation YYWaterflowView 31 32 #pragma mark-懒加载 33 -(NSMutableArray *)cellFrames 34 { 35 if (_cellFrames==nil) { 36 _cellFrames=[NSMutableArray array]; 37 } 38 return _cellFrames; 39 } 40 41 -(NSMutableDictionary *)displayingCells 42 { 43 if (_displayingCells==nil) { 44 _displayingCells=[NSMutableDictionary dictionary]; 45 } 46 return _displayingCells; 47 } 48 49 -(NSMutableSet *)reusableCells 50 { 51 if (_reusableCells==nil) { 52 _reusableCells=[NSMutableSet set]; 53 } 54 return _reusableCells; 55 } 56 57 - (id)initWithFrame:(CGRect)frame 58 { 59 self = [super initWithFrame:frame]; 60 if (self) { 61 } 62 return self; 63 } 64 65 -(void)willMoveToSuperview:(UIView *)newSuperview 66 { 67 [self reloadData]; 68 } 69 70 #pragma mark-公共方法 71 /** 72 * cell的宽度 73 */ 74 -(CGFloat)cellWidth 75 { 76 //cell的列数 77 int numberOfColumns=[self numberOfColumns]; 78 CGFloat leftM=[self marginForType:YYWaterflowViewMarginTypeLeft]; 79 CGFloat rightM=[self marginForType:YYWaterflowViewMarginTypeRight]; 80 CGFloat columnM=[self marginForType:YYWaterflowViewMarginTypeColumn]; 81 return (self.frame.size.width-leftM-rightM-(numberOfColumns-1)*columnM)/numberOfColumns; 82 } 83 84 /** 85 * 刷新数据 86 * 1.计算每个cell的frame 87 */ 88 -(void)reloadData 89 { 90 /* 91 (1)把字典中的所有的值,都从屏幕上移除 92 (2)清除字典中的所有元素 93 (3)清除cell的frame,每个位置的cell的frame都要重新计算 94 (4)清除可复用的缓存池。 95 */ 96 97 [self.displayingCells.allValues makeObjectsPerformSelector:@selector(removeFromSuperview)]; 98 [self.displayingCells removeAllObjects]; 99 [self.cellFrames removeAllObjects]; 100 [self.reusableCells removeAllObjects]; 101 102 //cell的总数是多少 103 int numberOfCells=[self.dadaSource numberOfCellsInWaterflowView:self]; 104 105 //cell的列数 106 int numberOfColumns=[self numberOfColumns]; 107 108 //间距 109 CGFloat leftM=[self marginForType:YYWaterflowViewMarginTypeLeft]; 110 111 CGFloat columnM=[self marginForType:YYWaterflowViewMarginTypeColumn]; 112 CGFloat topM=[self marginForType:YYWaterflowViewMarginTypeTop]; 113 CGFloat rowM=[self marginForType:YYWaterflowViewMarginTypeRow]; 114 CGFloat bottomM=[self marginForType:YYWaterflowViewMarginTypeBottom]; 115 116 //(1)cell的宽度 117 //cell的宽度=(整个view的宽度-左边的间距-右边的间距-(列数-1)X每列之间的间距)/总列数 118 // CGFloat cellW=(self.frame.size.width-leftM-rightM-(numberOfColumns-1)*columnM)/numberOfColumns; 119 CGFloat cellW=[self cellWidth]; 120 121 //用一个C语言的数组来存放所有列的最大的Y值 122 CGFloat maxYOfColumns[numberOfColumns]; 123 for (int i=0; i<numberOfColumns; i++) { 124 //初始化数组的数值全部为0 125 maxYOfColumns[i]=0.0; 126 } 127 128 129 //计算每个cell的fram 130 for (int i=0; i<numberOfCells; i++) { 131 132 //(2)cell的高度 133 //询问代理i位置的高度 134 CGFloat cellH=[self heightAtIndex:i]; 135 136 //cell处在第几列(最短的一列) 137 NSUInteger cellAtColumn=0; 138 139 //cell所处那列的最大的Y值(当前最短的那一列的最大的Y值) 140 //默认设置最短的一列为第一列(优化性能) 141 CGFloat maxYOfCellAtColumn=maxYOfColumns[cellAtColumn]; 142 143 //求出最短的那一列 144 for (int j=0; j<numberOfColumns; j++) { 145 if (maxYOfColumns[j]<maxYOfCellAtColumn) { 146 cellAtColumn=j; 147 maxYOfCellAtColumn=maxYOfColumns[j]; 148 } 149 } 150 151 //(3)cell的位置(X,Y) 152 //cell的X=左边的间距+列号*(cell的宽度+每列之间的间距) 153 CGFloat cellX=leftM+cellAtColumn*(cellW +columnM); 154 //cell的Y,先设定为0 155 CGFloat cellY=0; 156 if (maxYOfCellAtColumn==0.0) {//首行 157 cellY=topM; 158 }else 159 { 160 cellY=maxYOfCellAtColumn+rowM; 161 } 162 163 //设置cell的frame并添加到数组中 164 CGRect cellFrame=CGRectMake(cellX, cellY, cellW, cellH); 165 [self.cellFrames addObject:[NSValue valueWithCGRect:cellFrame]]; 166 167 //更新最短那一列的最大的Y值 168 maxYOfColumns[cellAtColumn]=CGRectGetMaxY(cellFrame); 169 } 170 171 //设置contentSize 172 CGFloat contentH=maxYOfColumns[0]; 173 for (int i=1; i<numberOfColumns; i++) { 174 if (maxYOfColumns[i]>contentH) { 175 contentH=maxYOfColumns[i]; 176 } 177 } 178 contentH += bottomM; 179 self.contentSize=CGSizeMake(0, contentH); 180 } 181 182 /** 183 * 当UIScrollView滚动的时候也会调用这个方法 184 */ 185 -(void)layoutSubviews 186 { 187 [super layoutSubviews]; 188 189 190 //向数据源索要对应位置的cell 191 NSUInteger numberOfCells=self.cellFrames.count; 192 for (int i=0; i<numberOfCells; i++) { 193 //取出i位置的frame,注意转换 194 CGRect cellFrame=[self.cellFrames[i] CGRectValue]; 195 196 //优先从字典中取出i位置的cell 197 YYWaterflowViewCell *cell=self.displayingCells[@(i)]; 198 199 //判断i位置对应的frame在不在屏幕上(能否看见) 200 if ([self isInScreen:cellFrame]) {//在屏幕上 201 if (cell==nil) { 202 cell= [self.dadaSource waterflowView:self cellAtIndex:i]; 203 cell.frame=cellFrame; 204 [self addSubview:cell]; 205 206 //存放在字典中 207 self.displayingCells[@(i)]=cell; 208 } 209 210 }else //不在屏幕上 211 { 212 if (cell) { 213 //从scrollView和字典中删除 214 [cell removeFromSuperview]; 215 [self.displayingCells removeObjectForKey:@(i)]; 216 217 //存放进缓存池 218 [self.reusableCells addObject:cell]; 219 } 220 } 221 } 222 // NSLog(@"%d",self.subviews.count); 223 } 224 225 -(id)dequeueReusableCellWithIdentifier:(NSString *)identifier 226 { 227 __block YYWaterflowViewCell *reusableCell=nil; 228 [self.reusableCells enumerateObjectsUsingBlock:^(YYWaterflowViewCell *cell, BOOL *stop) { 229 if ([cell.identifier isEqualToString:identifier]) { 230 reusableCell=cell; 231 *stop=YES; 232 } 233 }]; 234 235 if (reusableCell) {//从缓存池中移除(已经用掉了) 236 [self.reusableCells removeObject:reusableCell]; 237 } 238 return reusableCell; 239 } 240 241 #pragma mark cell的事件处理 242 -(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event 243 { 244 //如果没有点击事件的代理方法,那么就直接返回 245 if (![self.delegate respondsToSelector:@selector(waterflowView:didSelectAtIndex:)]) 246 return; 247 248 //获得手指在屏幕上点击的触摸点 249 UITouch *touch=[touches anyObject]; 250 CGPoint point1=[touch locationInView:touch.view]; 251 CGPoint point=[touch locationInView:self]; 252 NSLog(@"%@--%@",NSStringFromCGPoint(point),NSStringFromCGPoint(point1)); 253 254 __block NSNumber *selectIndex=nil; 255 [self.displayingCells enumerateKeysAndObjectsUsingBlock:^(id key, YYWaterflowViewCell *cell, BOOL *stop) { 256 if (CGRectContainsPoint(cell.frame, point)) { 257 selectIndex=key; 258 *stop=YES; 259 } 260 }]; 261 if (selectIndex) { 262 //需要转换 263 [self.delegate waterflowView:self didSelectAtIndex:selectIndex.unsignedIntegerValue]; 264 } 265 266 } 267 #pragma mark-私有方法 268 /** 269 * 判断一个人cell的frame有没有显示在屏幕上 270 */ 271 -(BOOL)isInScreen:(CGRect)frame 272 { 273 // return (CGRectGetMaxY(frame)>self.contentOffset.y)&&(CGRectGetMaxY(frame)<self.contentOffset.y+self.frame.size.height); 274 return (CGRectGetMaxY(frame) > self.contentOffset.y) && 275 (CGRectGetMinY(frame) < self.contentOffset.y + self.frame.size.height); 276 277 } 278 -(CGFloat)marginForType:(YYWaterflowViewMarginType)type 279 { 280 if ([self.delegate respondsToSelector:@selector(waterflowView:marginForType:)]) { 281 return [self.delegate waterflowView:self marginForType:type]; 282 }else 283 { 284 return YYWaterflowViewDefaultMargin; 285 } 286 } 287 288 -(NSUInteger)numberOfColumns 289 { 290 if ([self.dadaSource respondsToSelector:@selector(numberOfColumnsInWaterflowView:)]) { 291 return [self.dadaSource numberOfColumnsInWaterflowView:self]; 292 }else 293 { 294 return YYWaterflowViewDefaultNumberOfClunms; 295 } 296 } 297 298 -(CGFloat)heightAtIndex:(NSUInteger)index 299 { 300 if ([self.delegate respondsToSelector:@selector(waterflowView:heightAtIndex:)]) { 301 return [self.delegate waterflowView:self heightAtIndex:index]; 302 }else 303 { 304 return YYWaterflowViewDefaultCellH; 305 } 306 } 307 @end
二、刷新操作
刷新操作的代码设计:
1 // 2 // YYShopViewController.m 3 // 06-瀑布流 4 // 5 // Created by apple on 14-7-31. 6 // Copyright (c) 2014年 wendingding. All rights reserved. 7 // 8 9 #import "YYShopViewController.h" 10 #import "YYWaterflowView.h" 11 #import "YYWaterflowViewCell.h" 12 #import "YYShop.h" 13 #import "YYShopCell.h" 14 #import "MJExtension.h" 15 #import "MJRefresh.h" 16 17 @interface YYShopViewController ()<YYWaterflowViewDataSource,YYWaterflowViewDelegate> 18 @property(nonatomic,strong)NSMutableArray *shops; 19 @property(nonatomic,strong)YYWaterflowView *waterflowView; 20 @end 21 22 @implementation YYShopViewController 23 24 #pragma mark-懒加载 25 -(NSMutableArray *)shops 26 { 27 if (_shops==nil) { 28 _shops=[NSMutableArray array]; 29 } 30 return _shops; 31 } 32 - (void)viewDidLoad 33 { 34 [super viewDidLoad]; 35 36 //1.初始化数据 37 NSArray *newShop=[YYShop objectArrayWithFilename:@"2.plist"]; 38 [self.shops addObjectsFromArray:newShop]; 39 40 //2.创建一个瀑布流 41 YYWaterflowView *waterflow=[[YYWaterflowView alloc]init]; 42 waterflow.autoresizingMask=UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth; 43 waterflow.frame=self.view.bounds; 44 waterflow.delegate=self; 45 waterflow.dadaSource=self; 46 [self.view addSubview:waterflow]; 47 48 self.waterflowView=waterflow; 49 50 //3.实现数据的刷新 51 // [waterflow addFooterWithCallback:^{ 52 // NSLog(@"上拉数据刷新"); 53 // }]; 54 // 55 // [waterflow addHeaderWithCallback:^{ 56 // NSLog(@"下拉数据刷新"); 57 // }]; 58 59 [waterflow addHeaderWithTarget:self action:@selector(loadNewShops)]; 60 [waterflow addFooterWithTarget:self action:@selector(loadMoreShops)]; 61 } 62 63 -(void)loadNewShops 64 { 65 //模拟,只执行一次刷新操作 66 static dispatch_once_t onceToken; 67 dispatch_once(&onceToken, ^{ 68 //加载1.plist文件 69 NSArray *newShop=[YYShop objectArrayWithFilename:@"1.plist"]; 70 [self.shops insertObjects:newShop atIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, newShop.count)]]; 71 }); 72 73 //模拟网络延迟,2.0秒钟之后执行 74 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 75 //刷新数据 76 [self.waterflowView reloadData]; 77 78 //停止刷新 79 [self.waterflowView headerEndRefreshing]; 80 }); 81 } 82 83 -(void)loadMoreShops 84 { 85 static dispatch_once_t onceToken; 86 dispatch_once(&onceToken, ^{ 87 //加载1.plist文件 88 NSArray *newShop=[YYShop objectArrayWithFilename:@"3.plist"]; 89 [self.shops addObjectsFromArray:newShop]; 90 }); 91 92 //模拟网络延迟,2.0秒钟之后执行 93 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 94 //刷新数据 95 [self.waterflowView reloadData]; 96 97 //停止刷新 98 [self.waterflowView footerEndRefreshing]; 99 }); 100 101 } 102 103 #pragma mark-数据源方法 104 -(NSUInteger)numberOfCellsInWaterflowView:(YYWaterflowView *)waterflowView 105 { 106 return self.shops.count; 107 } 108 -(NSUInteger)numberOfColumnsInWaterflowView:(YYWaterflowView *)waterflowView 109 { 110 return 3; 111 } 112 -(YYWaterflowViewCell *)waterflowView:(YYWaterflowView *)waterflowView cellAtIndex:(NSUInteger)index 113 { 114 YYShopCell *cell=[YYShopCell cellWithwaterflowView:waterflowView]; 115 cell.shop=self.shops[index]; 116 return cell; 117 } 118 119 120 #pragma mark-代理方法 121 -(CGFloat)waterflowView:(YYWaterflowView *)waterflowView heightAtIndex:(NSUInteger)index 122 { 123 YYShop *shop=self.shops[index]; 124 //根据Cell的宽度和图片的宽高比 算出cell的高度 125 return waterflowView.cellWidth*shop.h/shop.w; 126 } 127 128 -(void)waterflowView:(YYWaterflowView *)waterflowView didSelectAtIndex:(NSUInteger)index 129 { 130 NSLog(@"点击了第%d个cell",index); 131 } 132 133 134 @end
实现的刷新效果:
三、竖屏和横屏调整
设置横屏和竖屏。
屏幕旋转完毕会调用下面的方法。
因为scrollView的宽度是固定的,没有改变。
设置view的宽度和高度可以跟随者父控件自动拉伸。(在iPad开发中会将常用到)
iOS开发UI篇—自定义瀑布流控件(蘑菇街数据刷新操作),布布扣,bubuko.com
时间: 2024-10-08 11:13:04