欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

iOS UICollectionView实现标签选择器

程序员文章站 2022-05-25 22:57:45
近来,在项目中需要实现一个类似兴趣标签的选择器。由于标签的文字长度不定,所以标签的显示长度就不定。为了实现效果,就使用了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的值了。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。