美文网首页
UICollectionView交换实践 2023-04-19

UICollectionView交换实践 2023-04-19

作者: 松哥888 | 来源:发表于2023-04-18 16:29 被阅读0次

简介

项目要求通过手势移动交换照片。照片有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];
    });

这是真的大坑,折腾了大半天才试出来

界面

企业微信截图_5c3892eb-f7b5-4be1-9504-2f09e38e58a2.png

参考文章

IOS 类似网易的频道选择功能、长按移动item、UICollectionView的高级使用

相关文章

网友评论

      本文标题:UICollectionView交换实践 2023-04-19

      本文链接:https://www.haomeiwen.com/subject/moohjdtx.html