玩了几天了,本来不打算写的,还是写下来留着自己以后可能会需要。本例是自己封装一个collectionView,使用时只需要实例化并传入数据源即可。就能进行相应的删除,移动和插入操作。效果如下:

1 本例中的collectionView实际是个UIView,上面放了个充满view的UICollectionView,并且collectionView的代理是UIView。
2 为了代码简洁,使用了一个枚举类型的methodType,来判断需要的操作,用KVO实时观测type的变化,这样就不需要写很多的接口方法,使用起来只需要修改type即可。
3 添加了一些动画效果让操作看起来更好看而且自然,自定义的FlowLayout是空的,如需自定义自行修改即可,自定义的Cell同理。
实例化方法中传入数据即可:
- (MDCollectionView *)collectionView{
if (!_collectionView) {
_collectionView = [[MDCollectionView alloc]initWithFrame:CGRectMake(0, 100, SW, SH-100) DataSource:self.dataSource];
}
return _collectionView;
}在自定义cell上添加了一个删除button,默认是hidden状态,当type为MDDelete时,reload列表,在cellWillDisplay中将删除button显示出来。
- (void)collectionView:(UICollectionView )collectionView willDisplayCell:(UICollectionViewCell )cell forItemAtIndexPath:(NSIndexPath )indexPath{
MDCollectionCell MDcell = (MDCollectionCell )cell;
if (_isShake) {
if (self.type == MDDelete) {
MDcell.deleteBtn.hidden = NO;
}else MDcell.deleteBtn.hidden = YES;
CAKeyframeAnimation keyAnimation = [CAKeyframeAnimation animation];
keyAnimation.keyPath = @"transform.rotation";
keyAnimation.values = @[@(-0.03),@(0.03)];
keyAnimation.repeatCount = MAXFLOAT;
keyAnimation.duration = 0.2f;
[MDcell.image.layer addAnimation:keyAnimation forKey:@"keyAnimation"];
}else if (!_isShake){
[MDcell.image.layer removeAllAnimations];
MDcell.deleteBtn.hidden = YES;
}
}
需要注意的是,删除是必须先remove掉数据源,然后在remove掉Item。
移动
移动在IOS9之后非常简单,有相应的方法实现,
需要注意的是,我们在移动时给collectionview添加了panGesture来移动,首先需要根据手的落点获取选中的item,那么就要判断,获取的IndexPath是否为空:
- (void)handleMethod:(UIPanGestureRecognizer )sender{
CGPoint location = [sender locationInView:self.collectionView];
NSIndexPath indexPath = [self.collectionView indexPathForItemAtPoint:location];
// location.x =
if (self.type == MDMove) {
if (indexPath) {
switch (sender.state) {
case UIGestureRecognizerStateBegan:
[self.collectionView beginInteractiveMovementForItemAtIndexPath:indexPath];
break;
case UIGestureRecognizerStateChanged:
[self.collectionView updateInteractiveMovementTargetPosition:[sender locationInView:self.collectionView]];
break;
default:
break;
}
}
if (sender.state == UIGestureRecognizerStateEnded) {
[self.collectionView endInteractiveMovement];
}
}需要注意的是,移动的时候必须在moveItem方法中先调整好数据源:
- (BOOL)collectionView:(UICollectionView )collectionView canMoveItemAtIndexPath:(NSIndexPath )indexPath{
return YES;
}
- (void)collectionView:(UICollectionView )collectionView moveItemAtIndexPath:(NSIndexPath )sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath{
id obj = [dataSource objectAtIndex:sourceIndexPath.row];
[dataSource removeObjectAtIndex:sourceIndexPath.row];
[dataSource insertObject:obj atIndex:destinationIndexPath.row];
}本例用插入女神周慧敏的照片作为例子,可按实际需要插入。
//以女神周慧敏照片做例子插入
else if (self.type == MDInsert){
if (!indexPath) {
indexPath = [self getIndexPathWithPoint:location];
if (sender.state == UIGestureRecognizerStateBegan) {
CGRect destinationFrame = [self.collectionView cellForItemAtIndexPath:indexPath].frame;
if (indexPath.row == dataSource.count) {
CGRect lastItemFrame = [self.collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForItem:dataSource.count-1 inSection:0]].frame;
destinationFrame = CGRectMake(lastItemFrame.origin.x+self.flowLayout.itemSize.width, lastItemFrame.origin.y, self.flowLayout.itemSize.width, self.flowLayout.itemSize.height);
}
UIImageView showImage = [[UIImageView alloc]initWithFrame:CGRectMake(SW/2-100, self.collectionView.contentOffset.y+SH/2 - 100, 200, 200)];
[showImage setImage:[UIImage imageNamed:@"vivian.jpg"]];
showImage.layer.cornerRadius = 5.0f;
showImage.layer.masksToBounds = YES;
[self.collectionView addSubview:showImage];
[UIView animateWithDuration:1 animations:^{
[showImage setFrame:destinationFrame];
}completion:^(BOOL finished) {
[showImage removeFromSuperview];
[dataSource insertObject:@"vivian.jpg" atIndex:indexPath.row];
[self.collectionView performBatchUpdates:^{
[self.collectionView insertItemsAtIndexPaths:@[indexPath]];
} completion:nil];
}];
}
}
}
由于插入的时候与移动相反,点击的是Item之外的空白处,尤其是点击最后一个Item之外的空白,就需要方法判断到底该插入到哪个位置:
//获取按压点该插入的IndexPath
- (NSIndexPath )getIndexPathWithPoint:(CGPoint)point{
if(![self.collectionView indexPathForItemAtPoint:CGPointMake(point.x+self.flowLayout.itemSize.width, point.y)] && [self.collectionView indexPathForItemAtPoint:CGPointMake(point.x, point.y+self.flowLayout.itemSize.height)]){
return [self.collectionView indexPathForItemAtPoint:CGPointMake(point.x, point.y+self.flowLayout.itemSize.height)];
}else if (![self.collectionView indexPathForItemAtPoint:CGPointMake(point.x, point.y+self.flowLayout.itemSize.height)] && [self.collectionView indexPathForItemAtPoint:CGPointMake(point.x+self.flowLayout.itemSize.width, point.y)]){
return [self.collectionView indexPathForItemAtPoint:CGPointMake(point.x+self.flowLayout.itemSize.width, point.y)];
}else{
return [NSIndexPath indexPathForItem:dataSource.count inSection:self.collectionView.numberOfSections - 1];
}
return nil;
}大致实现就是这样,具体请看源码。由于代理是collectionView,所以需要自己写一些代理方法将点击等事件传出来,可根据自己需要写。