简介
项目要求通过手势移动交换照片。照片有3~5张,也就是说拍摄照片的数量有后端接口决定,是动态的。所以用UICollectionView实现。
移动
UICollectionView的显示通过代理函数实现,但是移动却需要主动调用API实现。通过结合一个长按手势,实现如下:
/// 添加长按手势
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPressResponse:)];
[self.imageCollectionView addGestureRecognizer:longPress];
添加长按手势,可以在viewDidLoad中做
/// 长按手势;交换数据
- (void)longPressResponse:(UILongPressGestureRecognizer *)longPress {
switch (longPress.state) {
case UIGestureRecognizerStateBegan: {
/// 开始移动
CGPoint point = [longPress locationInView:self.imageCollectionView];
NSIndexPath *indexPath = [self.imageCollectionView indexPathForItemAtPoint:point];
[self.imageCollectionView beginInteractiveMovementForItemAtIndexPath:indexPath];
NSLog(@"开始移动:%ld", indexPath.row);
}
break;
case UIGestureRecognizerStateChanged: {
/// 刷新位置
CGPoint point = [longPress locationInView:self.imageCollectionView];
[self.imageCollectionView updateInteractiveMovementTargetPosition:point];
}
break;
case UIGestureRecognizerStateEnded: {
/// 结束移动
CGPoint point = [longPress locationInView:self.imageCollectionView];
NSIndexPath *indexPath = [self.imageCollectionView indexPathForItemAtPoint:point];
WGBNormalCellViewModel *viewModel = self.dataSource[indexPath.row];
if (viewModel.isAddNode) {
/// 取消移动
[self.imageCollectionView cancelInteractiveMovement];
NSLog(@"取消移动:%ld", indexPath.row);
} else {
/// 结束移动
[self.imageCollectionView endInteractiveMovement];
NSLog(@"结束移动:%ld", indexPath.row);
}
}
break;
default: {
/// 取消移动
[self.imageCollectionView cancelInteractiveMovement];
}
break;
}
}
手势处理方法中主动调用相关API进行单元格移动。
/// 取消移动[self.imageCollectionView cancelInteractiveMovement];
会让单元格回到原处;
定义是否能够移动
就算有手势,能否移动,也是是[self.imageCollectionView beginInteractiveMovementForItemAtIndexPath:indexPath]
这个API能否起作用,在代理方法中定义:
/// 移动:加号结点不可移动,普通结点可移动
- (BOOL)collectionView:(UICollectionView *)collectionView canMoveItemAtIndexPath:(NSIndexPath *)indexPath {
WGBNormalCellViewModel *viewModel = self.dataSource[indexPath.row];
if (viewModel.isAddNode) {
NSLog(@"canMoveItemAtIndexPath:%ld, NO", indexPath.row);
return NO;
} else {
NSLog(@"canMoveItemAtIndexPath:%ld, YES", indexPath.row);
return YES;
}
}
对于返回NO的添加结点,长按手势没有反应
数据源处理
在长按手势中主动调用[self.imageCollectionView beginInteractiveMovementForItemAtIndexPath:indexPath]
这个API进行移动,只是界面上发生了变化,但是底层的数据源并没有改变。这就很容易导致数据源和界面的错乱(是真的差劲的设计)。
数据源需要在代理方法中手动处理。
/// 移动结点:交换数据
- (void)collectionView:(UICollectionView *)collectionView moveItemAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath*)destinationIndexPath {
NSLog(@"sourceIndexPath:%ld", sourceIndexPath.row);
NSLog(@"destinationIndexPath:%ld", destinationIndexPath.row);
/// 交换数据源
[self doExchangeWithStartIndex:sourceIndexPath.row endIndex:destinationIndexPath.row];
/// 延时更新表格,不然的话界面还是交换前的
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (ino64_t)(1 * NSEC_PER_SEC));
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_after(time, queue, ^{
[self.imageCollectionView reloadData];
});
}
调用
[self.imageCollectionView endInteractiveMovement]
结束API,这里会回调;
调用[self.imageCollectionView cancelInteractiveMovement]
取消API,这里不会回调;
界面不更新的问题
数据源交换之后,如果直接调用 [self.imageCollectionView reloadData];
,界面有可能不更新。
采用延时更新的方式可以解决问题。原因不知道。
/// 延时更新表格,不然的话界面还是交换前的
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (ino64_t)(1 * NSEC_PER_SEC));
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_after(time, queue, ^{
[self.imageCollectionView reloadData];
});
这是真的大坑,折腾了大半天才试出来