iOS开发之功能模块--根据需求开发横向的子弹盒View
这个需求是本人工作开发中后期需求要添加的新功能,本人模仿UITableView的代理和数据源方法进行了第一阶段的开发。第二阶段是添加丰富的动画。
这个功能需求描述:能上传添加五个待选头像,五个头像分别都可以被设置为app的正式头像和展示封面,五个头像分别都可以删除,这个自定义View最后一个正方形提供可以添加图片的功能,每添加一个图片都排列在前面待选头像的后面。
花费时间:半天
完成之后的评价:1、需要优化,在父View是ScrollView或者UITableView的情况下,移动会不断调用layoutSubviews方法,性能不好,需要通过判断来处理优化这个逻辑。2、可以进入下一阶段实现更炫的交互动画体验,不过这个比较花时间。
第一阶段效果展示:
后来在新的项目中也成功的运用了:
接着上源码吧:
BulletBox.h
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.m
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