iOS UICollectionView实现标签选择器
程序员文章站
2023-02-22 08:49:33
近来,在项目中需要实现一个类似兴趣标签的选择器。由于标签的文字长度不定,所以标签的显示长度就不定。为了实现效果,就使用了uicollectionview来实现了每行的标签数量不定、cell的宽度自适应...
近来,在项目中需要实现一个类似兴趣标签的选择器。由于标签的文字长度不定,所以标签的显示长度就不定。为了实现效果,就使用了uicollectionview来实现了每行的标签数量不定、cell的宽度自适应的效果。先在此分享出来:
1、自适应uicollectionviewcell
这里只是在自适应uicollectionviewcell上放一个和uicollectionviewcell保持一样大小的按钮,当选中和取消选中时改变按钮的文字颜色和边框颜色:
#pragma mark---标签cell @implementation yltagscollectionviewcell -(instancetype)initwithframe:(cgrect)frame { if(self = [super initwithframe:frame]){ self.backgroundcolor = [uicolor clearcolor]; _btn = [uibutton buttonwithtype:uibuttontypecustom]; //此处可以根据需要自己使用自动布局代码实现 _btn.frame = cgrectmake(0, 0, frame.size.width, frame.size.height); _btn.backgroundcolor = [uicolor whitecolor]; _btn.titlelabel.font = [uifont systemfontofsize:14]; _btn.layer.borderwidth = 1.f; _btn.layer.cornerradius = frame.size.height/2.0; _btn.layer.maskstobounds = yes; [_btn settitlecolor:hexcolor(0x666666) forstate:uicontrolstatenormal]; _btn.layer.bordercolor = hexcolor(0xdddddd).cgcolor; _btn.userinteractionenabled = no; [self.contentview addsubview:_btn]; } return self; } -(void)layoutsubviews { [super layoutsubviews]; _btn.frame = cgrectmake(0, 0, self.contentview.frame.size.width, self.contentview.frame.size.height); } -(void)setselected:(bool)selected { [super setselected:selected]; _btn.layer.bordercolor = selected?hexcolor(0xffb400).cgcolor:hexcolor(0xdddddd).cgcolor; [_btn settitlecolor:selected?hexcolor(0xffb400):hexcolor(0x666666) forstate:uicontrolstatenormal]; } -(void)sethighlighted:(bool)highlighted { [super sethighlighted:highlighted]; _btn.layer.bordercolor = highlighted?hexcolor(0xffb400).cgcolor:hexcolor(0xdddddd).cgcolor; [_btn settitlecolor:highlighted?hexcolor(0xffb400):hexcolor(0x666666) forstate:uicontrolstatenormal]; } @end
2、uicollectionviewflowlayout子类--ylwaterflowlayout的实现
.h头文件
#import <uikit/uikit.h> @class ylwaterflowlayout; @protocol ylwaterflowlayoutdelegate <nsobject> /**通过代理获得每个cell的宽度*/ - (cgfloat)waterflowlayout:(ylwaterflowlayout *)layout widthatindexpath:(nsindexpath *)indexpath; @end @interface ylwaterflowlayout : uicollectionviewflowlayout @property (nonatomic,assign) id<ylwaterflowlayoutdelegate> delegate; @property(nonatomic,assign)cgfloat rowheight;///< 固定行高 @end
.m文件
#import "ylwaterflowlayout.h" @interface ylwaterflowlayout() @property(nonatomic,strong)nsmutablearray *originxarray; @property(nonatomic,strong)nsmutablearray *originyarray; @end @implementation ylwaterflowlayout #pragma mark - 初始化属性 - (instancetype)init { self = [super init]; if (self) { self.minimuminteritemspacing = 5;//同一行不同cell间距 self.minimumlinespacing = 5;//行间距 self.sectioninset = uiedgeinsetsmake(10, 10, 10, 10); self.scrolldirection = uicollectionviewscrolldirectionvertical; _originxarray = [nsmutablearray array]; _originyarray = [nsmutablearray array]; } return self; } #pragma mark - 重写父类的方法,实现瀑布流布局 #pragma mark - 当尺寸有所变化时,重新刷新 - (bool)shouldinvalidatelayoutforboundschange:(cgrect)newbounds { return yes; } - (void)preparelayout { [super preparelayout]; } #pragma mark - 处理所有的item的layoutattributes - (nsarray *)layoutattributesforelementsinrect:(cgrect)rect { nsarray *array = [super layoutattributesforelementsinrect:rect]; nsmutablearray *mutarray = [nsmutablearray arraywithcapacity:array.count]; for(uicollectionviewlayoutattributes *attrs in array){ uicollectionviewlayoutattributes *theattrs = [self layoutattributesforitematindexpath:attrs.indexpath]; [mutarray addobject:theattrs]; } return mutarray; } #pragma mark - 处理单个的item的layoutattributes - (uicollectionviewlayoutattributes *)layoutattributesforitematindexpath:(nsindexpath *)indexpath { cgfloat x = self.sectioninset.left; cgfloat y = self.sectioninset.top; //判断获得前一个cell的x和y nsinteger prerow = indexpath.row - 1; if(prerow >= 0){ if(_originyarray.count > prerow){ x = [_originxarray[prerow]floatvalue]; y = [_originyarray[prerow]floatvalue]; } nsindexpath *preindexpath = [nsindexpath indexpathforitem:prerow insection:indexpath.section]; cgfloat prewidth = [self.delegate waterflowlayout:self widthatindexpath:preindexpath]; x += prewidth + self.minimuminteritemspacing; } cgfloat currentwidth = [self.delegate waterflowlayout:self widthatindexpath:indexpath]; //保证一个cell不超过最大宽度 currentwidth = min(currentwidth, self.collectionview.frame.size.width - self.sectioninset.left - self.sectioninset.right); if(x + currentwidth > self.collectionview.frame.size.width - self.sectioninset.right){ //超出范围,换行 x = self.sectioninset.left; y += _rowheight + self.minimumlinespacing; } // 创建属性 uicollectionviewlayoutattributes *attrs = [uicollectionviewlayoutattributes layoutattributesforcellwithindexpath:indexpath]; attrs.frame = cgrectmake(x, y, currentwidth, _rowheight); _originxarray[indexpath.row] = @(x); _originyarray[indexpath.row] = @(y); return attrs; } #pragma mark - collectionview的滚动范围 - (cgsize)collectionviewcontentsize { cgfloat width = self.collectionview.frame.size.width; __block cgfloat maxy = 0; [_originyarray enumerateobjectsusingblock:^(nsnumber *number, nsuinteger idx, bool * _nonnull stop) { if ([number floatvalue] > maxy) { maxy = [number floatvalue]; } }]; return cgsizemake(width, maxy + _rowheight + self.sectioninset.bottom); } @end
实现思路:在ylwaterflowlayout中使用originxarray和originyarray两个个数组记录了每一个自定义yltagscollectionviewcell的位置x和y。
在 -(uicollectionviewlayoutattributes *)layoutattributesforitematindexpath:(nsindexpath *)indexpath方法中通获得与当前yltagscollectionviewcell临近的“上一个yltagscollectionviewcell”的位置和尺寸信息,将上一个cell的x加上上一个cell的width来得到当前cell的x。同时还要判断当前cell的x+width是否会超越出屏幕右边缘,如果超出,则表明需要换行显示了,这时候就要修改y的值了。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。