這個需求是本人工做開發中後期需求要添加的新功能,本人模仿UITableView的代理和數據源方法進行了第一階段的開發。第二階段是添加豐富的動畫。數組
這個功能需求描述:能上傳添加五個待選頭像,五個頭像分別均可以被設置爲app的正式頭像和展現封面,五個頭像分別均可以刪除,這個自定義View最後一個正方形提供能夠添加圖片的功能,每添加一個圖片都排列在前面待選頭像的後面。app
花費時間:半天佈局
完成以後的評價:一、須要優化,在父View是ScrollView或者UITableView的狀況下,移動會不斷調用layoutSubviews方法,性能很差,須要經過判斷來處理優化這個邏輯。二、能夠進入下一階段實現更炫的交互動畫體驗,不過這個比較花時間。性能
第一階段效果展現:優化
後來在新的項目中也成功的運用了:動畫
接着上源碼吧:ui
BulletBox.hatom
1 // 2 // BulletBox.h 3 // BulletBox 4 // 5 // Created by HEYANG on 16/8/19. 6 // Copyright © 2016年 HeYang. All rights reserved. 7 // 8 9 #import <UIKit/UIKit.h> 10 /* 11 ********************************************************************************* 12 * 🌟🌟🌟 Bullet子彈 🌟🌟🌟 13 * 需求:儘量的封裝,對外提供最簡單的需求須要的接口 14 ********************************************************************************* 15 */ 16 @interface Bullet : UIView 17 18 @property (nonatomic,weak)UIImageView *bulletImageView; 19 @property (nonatomic,assign)BOOL isDoCircleFrame;// 是否須要設置圓邊框 20 21 @end 22 23 24 /* 25 ********************************************************************************* 26 * 🌟🌟🌟 BulletBox子彈盒 🌟🌟🌟 27 * 需求:等高等長的的,就用屬性設置高度,不等高和長的,用代理方法,這裏暫時不寫出代理方法 28 * 模仿UITableView封裝,公開數據源和代理方法 29 ********************************************************************************* 30 */ 31 32 @protocol BulletBoxDataSource; 33 @protocol BulletBoxDelegate; 34 35 @interface BulletBox : UIView 36 37 @property (nonatomic,assign)CGFloat bulletBoxHeight;// 內部都是正方形,因此只要設置高 38 @property (nonatomic,weak)id<BulletBoxDataSource> datasource; 39 @property (nonatomic,weak)id<BulletBoxDelegate> delegate; 40 @property (nonatomic,assign)NSInteger bulletMaxCount; 41 @property (nonatomic,strong)NSMutableArray *imageArr; 42 @property (nonatomic,assign)CGFloat bulletSpace; 43 44 // 更新圖片數組,同時內部會根據數組刷新 45 - (void)updateImageArrs:(NSMutableArray*)imageArr; 46 47 @end 48 49 #pragma mark - 數據源方法+代理方法 50 @protocol BulletBoxDataSource <NSObject> 51 52 @required 53 // 建立Bullet的數據源方法 54 - (Bullet*)bulletBox:(BulletBox*)bulletBox bulletIndex:(NSInteger)index; 55 @optional 56 // 建立最後一個額外樣式的Bullet的數據源方法 57 - (Bullet*)addLastBulletInbulletBox:(BulletBox*)bulletBox; 58 59 @end 60 61 @protocol BulletBoxDelegate <NSObject> 62 63 // 點擊Bullet的代理方法,不包括最後一個額外樣式的Bullet 64 - (void)bulletBox:(BulletBox*)bulletBox didSelectIndex:(NSInteger)index; 65 // 點擊最後一個額外樣式的代理方法 66 - (void)didSelectLastBullet:(BulletBox*)bulletBox; 67 68 @end
BulletBox.mspa
1 // 2 // BulletBox.m 3 // BulletBox 4 // 5 // Created by HEYANG on 16/8/19. 6 // Copyright © 2016年 HeYang. All rights reserved. 7 // 8 9 #import "BulletBox.h" 10 11 12 /* 13 ********************************************************************************* 14 * 🌟🌟🌟 Bullet子彈 🌟🌟🌟 15 ********************************************************************************* 16 */ 17 #pragma mark - Bullet 18 @protocol BulletDelegate; 19 20 @interface Bullet () 21 22 @property (nonatomic,assign)NSInteger index; 23 @property (nonatomic,weak)id<BulletDelegate> delegate; 24 25 @end 26 27 @protocol BulletDelegate <NSObject> 28 29 - (void)bullet:(Bullet*)bullet; 30 31 @end 32 33 @implementation Bullet 34 35 - (instancetype)initWithFrame:(CGRect)frame 36 { 37 self = [super initWithFrame:frame]; 38 if (self) { 39 UIImageView *imageView = [UIImageView new]; 40 [self addSubview:imageView]; 41 self.bulletImageView = imageView; 42 self.backgroundColor = [UIColor clearColor]; 43 _isDoCircleFrame = NO; 44 } 45 return self; 46 } 47 48 - (void)addTapGestureRecognizer{ 49 // hy:先判斷當前是否有交互事件,若是沒有的話。。。全部gesture的交互事件都會被添加進gestureRecognizers中 50 if (![self gestureRecognizers]) { 51 self.userInteractionEnabled = YES; 52 // hy:添加單擊事件 53 UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tap)]; 54 [self addGestureRecognizer:tap]; 55 } 56 } 57 58 - (void)tap{ 59 if (_delegate && [_delegate respondsToSelector:@selector(bullet:)]) { 60 [_delegate bullet:self]; 61 } 62 } 63 64 - (void)layoutSubviews{ 65 self.bulletImageView.frame = self.bounds; 66 if (_isDoCircleFrame) { 67 self.bulletImageView.layer.masksToBounds = YES; 68 self.bulletImageView.layer.cornerRadius = self.bulletImageView.frame.size.width/2; 69 self.bulletImageView.layer.borderWidth = 1; 70 self.bulletImageView.layer.borderColor = [UIColor whiteColor].CGColor; 71 } 72 } 73 74 75 @end 76 /* 77 ********************************************************************************* 78 * 🌟🌟🌟 BulletBox子彈盒 🌟🌟🌟 79 ********************************************************************************* 80 */ 81 #pragma mark - BulletBox 82 @interface BulletBox () <BulletDelegate> 83 84 85 86 @end 87 88 @implementation BulletBox 89 90 - (instancetype)initWithFrame:(CGRect)frame 91 { 92 93 self = [super initWithFrame:frame]; 94 if (self) { 95 self.backgroundColor = [UIColor clearColor]; 96 } 97 return self; 98 } 99 100 - (void)layoutSubviews{ 101 [super layoutSubviews]; 102 DLog(@"從新佈局"); 103 NSInteger bulletCount = 0; 104 CGFloat bBH = 0.f; 105 if (_imageArr) { 106 // 獲取子彈的數量 107 bulletCount = self.imageArr.count; 108 bulletCount = bulletCount>_bulletMaxCount?_bulletMaxCount:bulletCount; 109 _imageArr = [NSMutableArray arrayWithArray:[_imageArr subarrayWithRange:NSMakeRange(0, bulletCount)]]; 110 DLog(@"%ld個子彈",bulletCount); 111 // 從新設置子彈盒的高度和長度 112 bBH = self.bulletBoxHeight; 113 CGRect frame = self.frame; 114 frame.size.height = bBH; 115 frame.size.width = (bBH+_bulletSpace) * bulletCount - _bulletSpace; 116 self.frame = frame; 117 // 添加子View以前,須要移除以前全部的子View 118 for (UIView* subview in self.subviews) { 119 [subview removeFromSuperview]; 120 } 121 if (_datasource && [_datasource respondsToSelector:@selector(bulletBox:bulletIndex:)]){ 122 for (NSInteger i=0; i<bulletCount; i++) { 123 Bullet *bullet = [_datasource bulletBox:self bulletIndex:i]; 124 if (bullet) { 125 [bullet addTapGestureRecognizer]; 126 bullet.delegate = self; 127 bullet.index = i; 128 bullet.frame = CGRectMake(i*(bBH+_bulletSpace), 0, bBH, bBH); 129 [self addSubview:bullet]; 130 } 131 } 132 } 133 } 134 // 根據需求添加最後一個View,可是若是達到了Max,這個最後一個就不須要顯示了 135 if (bulletCount < _bulletMaxCount) { 136 if (_datasource && [_datasource respondsToSelector:@selector(addLastBulletInbulletBox:)]){ 137 Bullet *bullet = [_datasource addLastBulletInbulletBox:self]; 138 if (bullet) { 139 CGRect frame = self.frame; 140 frame.size.width = (bBH+_bulletSpace) * (bulletCount+1) - _bulletSpace; 141 self.frame = frame; 142 if (![bullet gestureRecognizers]) { 143 bullet.userInteractionEnabled = YES; 144 // hy:添加單擊事件 145 UITapGestureRecognizer *bulletBoxTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(bulletBoxTap)]; 146 [bullet addGestureRecognizer:bulletBoxTap]; 147 bullet.frame = CGRectMake(bulletCount*(bBH+_bulletSpace), 0, bBH, bBH); 148 [self addSubview:bullet]; 149 } 150 } 151 } 152 } 153 if (self.superview) { 154 CGPoint center = self.center; 155 center.x = self.superview.bounds.size.width*0.5; 156 self.center = center; 157 } 158 } 159 160 #pragma mark - 代理方法 161 -(void)bullet:(Bullet *)bullet{ 162 if (_delegate && [_delegate respondsToSelector:@selector(bulletBox:didSelectIndex:)]) { 163 [_delegate bulletBox:self didSelectIndex:bullet.index]; 164 } 165 } 166 167 #pragma mark - 懶加載 168 - (CGFloat)bulletBoxHeight{ 169 if (_bulletBoxHeight <= 0) { 170 _bulletBoxHeight = 50; 171 } 172 return _bulletBoxHeight; 173 } 174 175 #pragma mark - 私有方法 176 - (void)bulletBoxTap{ 177 if (_delegate && [_delegate respondsToSelector:@selector(didSelectLastBullet:)]) { 178 [_delegate didSelectLastBullet:self]; 179 } 180 } 181 182 #pragma mark - public 183 //- (void)removeBulletIndex:(NSInteger)index{ 184 // [self.imageArr removeObjectAtIndex:index]; 185 // [self setNeedsLayout]; 186 //} 187 //- (void)insertBulletInLast:(NSString*)bulletImage{ 188 // [self.imageArr addObject:bulletImage]; 189 // [self setNeedsLayout]; 190 //} 191 - (void)updateImageArrs:(NSMutableArray*)imageArr{ 192 for (NSString *strURL in imageArr) { 193 DLog(@"str:%@",strURL); 194 } 195 self.imageArr = imageArr; 196 [self setNeedsLayout]; 197 } 198 199 200 @end