首先,上个效果图,效果图中主要有3个功能,第一个是不定长度的流式布局,第二个是自定义长按菜单,第三个是修改数据源并刷新布局,下面我会分开讲述。
效果图.gif-
自定义布局
自定义布局需要继承
UICollectionViewLayout
,流式布局也可以继承自UICollectionViewFlowLayout
,我自定义的类是继承自UICollectionViewLayout
的。自定义布局需要实现以下几个方法:
-
prepareLayout
布局之前的一些计算
-
collectionViewContentSize
返回应用该布局的collectionView的内容大小
-
layoutAttributesForElementsInRect
返回可见区域内的布局属性数组
-
layoutAttributesForItemAtIndexPath
某一个item的布局属性
一般在
prepareLayout
里面将contentSize和布局属性计算好,然后在方法中返回. -
-
自定义长按菜单
说到长按,可能有朋友会想到有在Cell里面添加长按手势,我开始也是这样想的,结果添加的手势,点击后并没有进入响应方法。后来在
UICollectionView
的回调方法里面找到了下面几个方法:// These methods provide support for copy/paste actions on cells. // All three should be implemented if any are. - (BOOL)collectionView:(UICollectionView *)collectionView shouldShowMenuForItemAtIndexPath:(NSIndexPath *)indexPath; - (BOOL)collectionView:(UICollectionView *)collectionView canPerformAction: (SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender: (nullable id)sender; - (void)collectionView:(UICollectionView *)collectionView performAction: (SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender: (nullable id)sender;
实现完几个方法,初步效果是有了,但是发现,菜单只有3个选项
copy,cut,paste
,并不能自定义菜单项,看来要自定义,只能另辟蹊径了。下面我最终的解决方案,贴代码了:
ViewController.m 文件 UIMenuItem *menuItem = [[UIMenuItem alloc] initWithTitle:@"删除" action:@selector(deleteAction:)]; [[UIMenuController sharedMenuController] setMenuItems:@[menuItem]]; 这里的deleteAction方法可以空实现,因为最后调用的是Cell里面的方法。
关键是自定义Cell里面
自定义 UICollectionViewCell.m 文件 -(BOOL)canBecomeFirstResponder{ return YES; } -(BOOL)becomeFirstResponder{ return YES; } -(BOOL)canPerformAction:(SEL)action withSender:(id)sender{ if([NSStringFromSelector(action) isEqualToString:@"deleteAction:"]){ return YES; } return NO; } -(void)deleteAction:(id)sender{ UICollectionView *collectionView = (UICollectionView *)[self superview]; if([collectionView isKindOfClass:[UICollectionView class]]){ id<UICollectionViewDelegate> collDelegate = collectionView.delegate; if([collDelegate respondsToSelector:@selector(collectionView:performAction:forItemAtIndexPath:withSender:)]){ NSIndexPath *indexPath = [collectionView indexPathForCell:self]; [collDelegate collectionView:collectionView performAction:@selector(deleteItem:) forItemAtIndexPath:indexPath withSender:sender]; } } }
然后执行
UICollectionView
的回调方法-(BOOL)collectionView:(UICollectionView *)collectionView shouldShowMenuForItemAtIndexPath:(NSIndexPath *)indexPath{ return YES; } -(BOOL)collectionView:(UICollectionView *)collectionView canPerformAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender{ return YES; } -(void)collectionView:(UICollectionView *)collectionView performAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender{ if([NSStringFromSelector(action) isEqualToString:@"deleteItem:"]){ NSLog(@"delete action"); [_sourceList removeObjectAtIndex:indexPath.item]; [collectionView reloadData]; } }
OK,自定义长按菜单已经完成,本来以为一切都OK了,但是当我点击删除菜单,执行
reloadData
之后,程序崩溃了...
-
刷新布局崩溃
在stackOverFlow里面也有看到这个问题,都说是调用
[collectionView.collectionViewLayout invalidateLayout]
这个方法,有的在reloadData之前的,有的在回调方法里面的,但最终还是没有解决这个问题。导致这个问题出现的原因是,因为每个item的大小都不一样,新增或者删除一个item,刷新布局之后,item的位置发生了变化。刷新布局后,会重新调用
prepareLayout
方法计算布局信息,所以在开始要先清除缓存的布局信息,将数组清空。[self.layoutArray removeAllObjects];
好了,总算是写完了,一直不擅长用文字表达,也不知道大家能不能看明白,马马虎虎,将就一下吧,希望大家多多支持,不明白的地方欢迎留言。
网友评论