需求:
需要的Button 的个数不一定 Button上显示的字符串的长度不一定,Button上显示的字符串和Button的个数全部从服务器下发 ,当Button的个数在屏幕上展示不下的时候要可以左右滑动 以展示更多的Button
方案:
综合以上因素 我选择利用CollectionView实现此功能.
其中遇到几个问题:
首先说明: cell中有一个Label 属性
- (
UILabel
*)textLabel{
if (! _textLabel ) {
_textLabel = [[ UILabel alloc ] initWithFrame : self . contentView . frame ];
_textLabel . layer . borderWidth = 1 ;
_textLabel . layer . borderColor = [ UIColor lightGrayColor ]. CGColor ;
_textLabel . font = [ UIFont systemFontOfSize : 13 ];
_textLabel . textAlignment = NSTextAlignmentCenter ;
_textLabel . textColor = [ UIColor blackColor ];
[ self . contentView addSubview : _textLabel ];
if (! _textLabel ) {
_textLabel = [[ UILabel alloc ] initWithFrame : self . contentView . frame ];
_textLabel . layer . borderWidth = 1 ;
_textLabel . layer . borderColor = [ UIColor lightGrayColor ]. CGColor ;
_textLabel . font = [ UIFont systemFontOfSize : 13 ];
_textLabel . textAlignment = NSTextAlignmentCenter ;
_textLabel . textColor = [ UIColor blackColor ];
[ self . contentView addSubview : _textLabel ];
}
_textLabel
.
frame
=
CGRectMake
(
CGRectGetMinX
(
self
.
contentView
.
frame
),
CGRectGetMinY
(
self
.
contentView
.
frame
),
self
.
frame
.
size
.
width
,
self
.
frame
.
size
.
height
);
return
_textLabel
;
}
问题 一 :因为选中cell时 cell的字和边框要变成红色 ,当再点击其他Item时此item要变为原来的样子
这个功能主要在collectionView的两个代理方法中实现
- (
void
)collectionView:(
UICollectionView
*)collectionView didSelectItemAtIndexPath:(
NSIndexPath
*)indexPath;
- (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath;;
具体实现如下:
/**
* 当点击 item 时会调用此方法 在此方法中把点击的 item 的 textLabel 属性的字体颜色和边框改变颜色
*
*/
- (void)collectionView:(UICollectionView
*)collectionView didSelectItemAtIndexPath:(
NSIndexPath
*)indexPath{
JWCCollectionViewCell
*cell = (
JWCCollectionViewCell
*)[collectionView
cellForItemAtIndexPath
:indexPath];
cell. textLabel . textColor = [ UIColor redColor ];
cell. textLabel . layer . borderColor = [ UIColor redColor ]. CGColor ;
}
/**
cell. textLabel . textColor = [ UIColor redColor ];
cell. textLabel . layer . borderColor = [ UIColor redColor ]. CGColor ;
}
/**
*
当点击其他
cell
时调用此方法
比如点击第一个
cell
的时候调用上面的方法,当点击第二个的时候先调用此方法,然后再调用上面的方法
在此方法中获取第一次点击的
cell
即
JWCCollectionViewCell *cell = (JWCCollectionViewCell *)[collectionView cellForItemAtIndexPath:indexPath];
把此
cell
的
textLabel
属性变回原来的样子
然后系统会调用上面的代理方法把点击的第二个
cell
的
textLabel
属性的字体颜色和边框改变颜色
这样就实现了
点击此
cell
改变颜色
再点击其他的
cell
是时
上一个点击的
cell
恢复到原来的样子
*
*/
*/
- (void)collectionView:(UICollectionView
*)collectionView didDeselectItemAtIndexPath:(
NSIndexPath
*)indexPath{
JWCCollectionViewCell
*cell = (
JWCCollectionViewCell
*)[collectionView
cellForItemAtIndexPath
:indexPath];
cell. textLabel . textColor = [ UIColor blackColor ];
cell. textLabel . layer . borderColor = [ UIColor lightGrayColor ]. CGColor ;
cell. textLabel . textColor = [ UIColor blackColor ];
cell. textLabel . layer . borderColor = [ UIColor lightGrayColor ]. CGColor ;
}
问题二:
假如cell的个数太多的话肯定会发生复用的问题:
比如数:屏幕的宽度只能显示3个cell 但是现在有四个需要显示,那么当点击第一个cell 向左滑动时会显示第四个 这个时候第四个item就是从复用队列中取出一个cell ,那么这个cell可能还保留这上一个的属性 比如说字体是红色的 其宽度可能会很大,不适合当前的string的宽度 ,左右滑动的时候cell 之间的间隔也会发生错乱 这绝不是想要的结果 。出现这个原因主要是cell的复用产生的。
解决方法:
在cell复用之前把cell恢复到初始化状态,那么这就要重写
- (
void
)prepareForReuse方法
具体做法如下:
- (
void
)prepareForReuse{
[ super prepareForReuse ];
_textLabel . frame = self . contentView . frame ;
_textLabel . layer . borderWidth = 1 ;
_textLabel . textColor = [ UIColor blackColor ];
_textLabel . layer . borderColor = [ UIColor lightGrayColor ]. CGColor ;
[ super prepareForReuse ];
_textLabel . frame = self . contentView . frame ;
_textLabel . layer . borderWidth = 1 ;
_textLabel . textColor = [ UIColor blackColor ];
_textLabel . layer . borderColor = [ UIColor lightGrayColor ]. CGColor ;
}
这样就解决了颜色问题
注意:
prepareForReuse
这个方法是CollectionViewCell的方法
但是错乱问题仍然没有解决:这主要是因为在定义
_textLabel 时它的frame设置问题 应该在添加一句
_textLabel
.
frame
=
CGRectMake
(
CGRectGetMinX
(
self
.
contentView
.
frame
),
CGRectGetMinY
(
self
.
contentView
.
frame
),
self
.
frame
.
size
.
width
,
self
.
frame
.
size
.
height
);
具体是这样的
----------------------------------------------------------------------------------------------
- (
UILabel
*)textLabel{
if (! _textLabel ) {
_textLabel = [[ UILabel alloc ] initWithFrame : self . contentView . frame ];
_textLabel . layer . borderWidth = 1 ;
_textLabel . layer . borderColor = [ UIColor lightGrayColor ]. CGColor ;
_textLabel . font = [ UIFont systemFontOfSize : 13 ];
_textLabel . textAlignment = NSTextAlignmentCenter ;
_textLabel . textColor = [ UIColor blackColor ];
[ self . contentView addSubview : _textLabel ];
if (! _textLabel ) {
_textLabel = [[ UILabel alloc ] initWithFrame : self . contentView . frame ];
_textLabel . layer . borderWidth = 1 ;
_textLabel . layer . borderColor = [ UIColor lightGrayColor ]. CGColor ;
_textLabel . font = [ UIFont systemFontOfSize : 13 ];
_textLabel . textAlignment = NSTextAlignmentCenter ;
_textLabel . textColor = [ UIColor blackColor ];
[ self . contentView addSubview : _textLabel ];
}
_textLabel
.
frame
=
CGRectMake
(
CGRectGetMinX
(
self
.
contentView
.
frame
),
CGRectGetMinY
(
self
.
contentView
.
frame
),
self
.
frame
.
size
.
width
,
self
.
frame
.
size
.
height
);
return
_textLabel
;
}
----------------------------------------------------------------------------------------------
问题三:
比如说 当点击第一个cell 后 向左滑动让第一个消失在界面中,当再滑出界面时你会发现它的选中状态消失了,而你并没有选中其他的cell ,这是为什么???因为上一问题的解决方法导致了这一步。那么该怎么样解决才能不至于解决上面的问题引出下面的问题呢??方法如下:
1, 定义一个全局变量:
@property
(
nonatomic
,
assign
)
NSInteger
selectIndex;
2 这个先给它赋一个永远也不可能达到的值
self
.
selectIndex
=
MAXFLOAT
;至于为什么赋这么大下面说
3,在
- (
void
)collectionView:(
UICollectionView
*)collectionView didSelectItemAtIndexPath:(
NSIndexPath
*)indexPath;这个代理方法中把
indexPath.
row 值赋值给
self
.
selectIndex
如下
self
.
selectIndex
= indexPath.
row
; 这就记住了所选择的item的位置
4,在
- (
UICollectionViewCell
*)collectionView:(
UICollectionView
*)collectionView cellForItemAtIndexPath:(
NSIndexPath
*)indexPath;这个方法中可能会从复用队列中去处cell 产生复用 但在复用之前会调用
- (
void
)prepareForReuse方法 又会恢复原样使选中状态消失,这个时候要判断一下,判断这个方法中的
indexPath.
row 是否等于
self
.
selectIndex 如果等于 使其变为选中的状态,具体代码如下:
- (
UICollectionViewCell
*)collectionView:(
UICollectionView
*)collectionView cellForItemAtIndexPath:(
NSIndexPath
*)indexPath {
JWCCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier : @"cellID" forIndexPath :indexPath ];
if (indexPath. row == _selectIndex ) {
cell. textLabel . textColor = [ UIColor redColor ];
cell. textLabel . layer . borderColor = [ UIColor redColor ]. CGColor ;
}
cell. textLabel . text = [ self . dataSourceArr objectAtIndex :indexPath. item ];
NSLog ( @"%@" ,cell. textLabel . text );
return cell;
JWCCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier : @"cellID" forIndexPath :indexPath ];
if (indexPath. row == _selectIndex ) {
cell. textLabel . textColor = [ UIColor redColor ];
cell. textLabel . layer . borderColor = [ UIColor redColor ]. CGColor ;
}
cell. textLabel . text = [ self . dataSourceArr objectAtIndex :indexPath. item ];
NSLog ( @"%@" ,cell. textLabel . text );
return cell;
}
这样即使选中的cell 消失在界面中在出现的时候也不会改变选中状态。
为什么要把
self
.
selectIndex
=
MAXFLOAT
如果不这样赋值 假如赋的值为0 那么每次打开时第一个cell就是选中状态,但是你并没有点击第一个。这主要是在cell生成的代理方法中的这一句代码引起的
if
(indexPath.
row
==
_selectIndex
) {
cell. textLabel . textColor = [ UIColor redColor ];
cell. textLabel . layer . borderColor = [ UIColor redColor ]. CGColor ;
cell. textLabel . textColor = [ UIColor redColor ];
cell. textLabel . layer . borderColor = [ UIColor redColor ]. CGColor ;
}
所以把
self
.
selectIndex
赋值为
MAXFLOAT 这样就不会有当一启动APP的时候就有cell被选中的状态
问题四:
根据string的宽度不同来生成的cell的宽度也不同的功能是在
- (
CGSize
)collectionView:(
UICollectionView
*)collectionView layout:(
UICollectionViewLayout
*)collectionViewLayout sizeForItemAtIndexPath:(
NSIndexPath
*)indexPath;
实在这个方法中返回不通的
CGSize 来改变每一个cell的宽度。
在此可以计算字符串的宽度:
具体的方法如下:
#pragma mark - UICollectionViewFlowLayoutDelegate
- ( CGSize )collectionView:( UICollectionView *)collectionView layout:( UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:( NSIndexPath *)indexPath{
NSString *str = [ self . dataSourceArr objectAtIndex :indexPath. item ];
CGRect rect = [ self getStringRect :str withFont : 13 ];
return CGSizeMake (rect. size . width , 30 );
}
/**
- ( CGSize )collectionView:( UICollectionView *)collectionView layout:( UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:( NSIndexPath *)indexPath{
NSString *str = [ self . dataSourceArr objectAtIndex :indexPath. item ];
CGRect rect = [ self getStringRect :str withFont : 13 ];
return CGSizeMake (rect. size . width , 30 );
}
/**
* 计算字符串的宽度的方法
*/
- ( CGRect )getStringRect:( NSString *)aString withFont :( CGFloat )font
{
CGRect rect;
if (aString){
CGRect rect = [aString boundingRectWithSize : CGSizeMake ( MAXFLOAT , 30 ) options : NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading attributes : @{ NSFontAttributeName : [ UIFont systemFontOfSize :font] } context : nil ];
return rect;
}
return rect;
- ( CGRect )getStringRect:( NSString *)aString withFont :( CGFloat )font
{
CGRect rect;
if (aString){
CGRect rect = [aString boundingRectWithSize : CGSizeMake ( MAXFLOAT , 30 ) options : NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading attributes : @{ NSFontAttributeName : [ UIFont systemFontOfSize :font] } context : nil ];
return rect;
}
return rect;
}
注意注意:!!!!
- (
CGRect
)getStringRect:(
NSString
*)aString withFont :(
CGFloat
)font 在这个方法中需要传入字体的大小。
这个大小一定要和 textLabel初始化时的字体大小一样 比如本例中在初始化的时候是13 那么在计算字符串的宽度的时候一定要是13 不然计算出的字符串的宽度比实际的要长 :会出现的问题是当左右滑动时会出现各个cell之间的间隔会出现改变 也许会两个cell会出现重叠!!
_textLabel
.
font
= [
UIFont
systemFontOfSize
:
13
];