1. 程式人生 > >iOS開發UI篇—自定義瀑布流控制元件(蘑菇街資料重新整理操作)

iOS開發UI篇—自定義瀑布流控制元件(蘑菇街資料重新整理操作)

  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