热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

取消拖动会话后,UICollectionView单元格会重复显示

如何解决《取消拖动会话后,UICollectionView单元格会重复显示》经验,需要怎么解决?

我遇到了一个在UICollectionViewController中实现本地拖放的模糊问题.集合视图允许将项目拖入和拖出文件夹.所有外观和工作都很好,直到取消拖动执行.

重现步骤:

    在拖动会话中收集多个项目.

    取消拖动,例如移动到屏幕边缘.

    在拖动会话中收集多个项目(可以包含上面取消的一些项目).

    将项目放入集合视图的更上方的文件夹中,以便会话中删除的项目导致重新定位已取消的拖动中的项目.

取消会话中的项目现在似乎位于其他单元格后面.它们不是真正的细胞,正如可以通过旋转iPad看到的那样,这些"幽灵"细胞不能正确旋转它们背后的真实细胞,它们旋转就好像它们的原点是固定的一样.它们也无法以任何方式进行交互,并且数据源方法不会对它们进行请求.在某些情况下,他们也可以在真实细胞结束后通过向上拉视图来看到它们(在放开后它弹回来隐藏它们).即使大部分真实单元格被拖出集合视图,大量取消的拖动也会导致非常差的滚动性能.

我已经尝试在故事板中重新创建整个UICollectionViewController及其单元格.这不是设置所有单元格属性(我希望它是)的情况,因为鬼影单元格包含所有元素.它们也可以多次覆盖,具体取决于所执行的操作.请看截图.

将数百个项目广泛拖入和拖出集合视图时,未发现任何问题.只有在取消拖动后才会出现问题.

我在这个阶段的最佳猜测是,ghost单元格是取消动画的一部分,在完成时尚未删除.鉴于没有API与拖放动画的交互,我不知道如何测试或修复它.

我在控制器中实现的内容:

    itemsForBeginningDragSession和itemsForAddingToDragSession,它们都将单个NSItemProvider添加到返回的数组中.

    dragPreviewParametersForItemAtIndexPath设置可见路径.(删除此方法对问题没有影响)

    如果会话不是本地会话,dropSessionDidUpdate将返回UIDropOperationMove,UICollectionViewDropIntentInsertIntoDestinationIndexPath或UIDropOperationForbidden.

    performDropWithCoordinator使用performBatchUpdates并为每个项调用dropItem.(问题仍然存在,没有dropItem和一个脏集合视图reloadSections!)

我在用什么:

Xcode 9.2,iOS 11.2和11.2.5的各种iPad

我现在已经坚持了五天,所以任何指向正确方向的人都会非常感激.

细胞重叠两次(三种情况下相似的图像):

细胞重叠两次

细胞重叠三次(第一个细胞有三个独特的图像):

在此输入图像描述

细胞多次重叠:

在此输入图像描述

下面是将文件拖动到现有文件夹的简单案例的代码,因为这使源更容易遵循:

#pragma mark - UICollectionViewDragDelegate

- (nonnull NSArray *)collectionView:(nonnull UICollectionView *)collectionView itemsForBeginningDragSession:(nonnull id)session atIndexPath:(nonnull NSIndexPath *)indexPath
{
    return [self itemsForDragSession:session atIndexPath:indexPath];
}

- (NSArray *)collectionView:(UICollectionView *)collectionView itemsForAddingToDragSession:(id)session atIndexPath:(NSIndexPath *)indexPath point:(CGPoint)point
{
    return [self itemsForDragSession:session atIndexPath:indexPath];
}

- (nonnull NSArray *)itemsForDragSession:(nonnull id)session atIndexPath:(nonnull NSIndexPath *)indexPath
{
    NSMutableArray *dragItems = [NSMutableArray array];

    if (self.isEditing)
    {
        return dragItems;
    }

    DrawingFile *drawingFile = [[WDDrawingManager sharedInstance] drawingFileAtIndex:indexPath.item];
    if (drawingFile && drawingFile.isDrawing)
    {
        NSItemProvider *itemProvider = [[NSItemProvider alloc] init];
        UIDragItem *dragItem = [[UIDragItem alloc] initWithItemProvider:itemProvider];

        [dragItems addObject:dragItem];
    }

    return dragItems;
}

- (UIDragPreviewParameters *)collectionView:(UICollectionView *)collectionView dragPreviewParametersForItemAtIndexPath:(NSIndexPath *)indexPath
{
    DrawingThumbnailCell *cell = (DrawingThumbnailCell *)[self.collectionView cellForItemAtIndexPath:indexPath];
    UIImageView *thumbnailView = cell.thumbnailImage;
    CGRect rect = [cell convertRect:thumbnailView.bounds fromView:thumbnailView];

    UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:7];

    UIDragPreviewParameters *parameters = [[UIDragPreviewParameters alloc] init];
    parameters.visiblePath = path;

    return parameters;
}

#pragma mark - UICollectionViewDropDelegate

- (UICollectionViewDropProposal *)collectionView:(UICollectionView *)collectionView dropSessionDidUpdate:(id)session withDestinationIndexPath:(NSIndexPath *)destinationIndexPath
{
    return [[UICollectionViewDropProposal alloc] initWithDropOperation:UIDropOperationMove intent:UICollectionViewDropIntentInsertIntoDestinationIndexPath];
}

- (void)collectionView:(nonnull UICollectionView *)collectionView performDropWithCoordinator:(nonnull id)coordinator
{
    // Check for local only
    if (coordinator.session.localDragSession == nil)
    {
        NSLog(@"performDropWithCoordinator: localDragSession == nil");
        return;
    }

    // Can only drag onto existing cells
    if (coordinator.destinatiOnIndexPath== nil)
    {
        NSLog(@"performDropWithCoordinator: destinatiOnIndexPath== nil");
        return;
    }

    // Check for dragging back to source
    if (coordinator.items.count == 1 &&
        [coordinator.destinationIndexPath isEqual:coordinator.items.firstObject.sourceIndexPath])
    {
        NSLog(@"performDropWithCoordinator: destinatiOnIndexPath== sourceIndexPath");
        return;
    }

    // TODO Support drop into new folder (limiting to existing keeps the test case simpler)
    DrawingFile *destinatiOnFile= [[WDDrawingManager sharedInstance] drawingFileAtIndex:coordinator.destinationIndexPath.item];
    if (destinationFile.isDrawing == YES)
    {
        NSLog(@"performDropWithCoordinator: destinationIndexPath is drawing");
        return;
    }

    // Collect the drawings and their index paths
    NSMutableArray *sourceFiles = [NSMutableArray array];
    NSMutableArray *sourceIndexPaths = [NSMutableArray array];
    for (id item in coordinator.items)
    {
        DrawingFile *sourceFile = [[WDDrawingManager sharedInstance] drawingFileAtIndex:item.sourceIndexPath.item];
        [sourceFiles addObject:sourceFile];
        [sourceIndexPaths addObject:item.sourceIndexPath];
    }

    [collectionView performBatchUpdates:^()
     {
         [[WDDrawingManager sharedInstance] moveSourceFiles:sourceFiles toDestinationFile:destinationFile];
         [collectionView deleteItemsAtIndexPaths:sourceIndexPaths];
     }
                             completion:nil];

    // Animate the cells moved
    DrawingThumbnailCell *cell = (DrawingThumbnailCell *)[collectionView cellForItemAtIndexPath:coordinator.destinationIndexPath];
    CGRect thumbnailRect = [cell convertRect:cell.thumbnailImage.bounds fromView:cell.thumbnailImage];
    for (id item in coordinator.items)
    {
        [coordinator dropItem:item.dragItem intoItemAtIndexPath:coordinator.destinationIndexPath rect:thumbnailRect];
    }

    // Update the folder drawing count
    [collectionView reloadItemsAtIndexPaths:@[coordinator.destinationIndexPath]];
}

记录会话1-1.请注意,draggingItems(6)的数量与cellAppearanceStatesByIndexPaths(6)中的条目数相匹配,无论是什么:

2018-02-20 14:25:07.104388+0000 Test[644:489734] performDropWithCoordinator: coordinator[<_UICollectionViewDragAndDropController:0x104e43640 -sessiOnKind= unsupported; reorderedItems = (
); shadowUpdates=(
); cellAppearanceStatesByIndexPaths={
    " {length = 2, path = 0 - 12}" = "<_UICollectionViewCellAppearanceState:0x112111400 - dragState = dragging; appearance = ghosted>";
    " {length = 2, path = 0 - 18}" = "<_UICollectionViewCellAppearanceState:0x11682e330 - dragState = dragging; appearance = ghosted>";
    " {length = 2, path = 0 - 13}" = "<_UICollectionViewCellAppearanceState:0x116d43bc0 - dragState = dragging; appearance = ghosted>";
    " {length = 2, path = 0 - 17}" = "<_UICollectionViewCellAppearanceState:0x11682b090 - dragState = dragging; appearance = ghosted>";
    " {length = 2, path = 0 - 19}" = "<_UICollectionViewCellAppearanceState:0x1121d2760 - dragState = dragging; appearance = ghosted>";
};  cellAppearanceStatesByCellPointers=NSMapTable {
}
;  placeholderCOntexts= {(
)}; source=<_UICollectionViewDragSourceController:0x104e75d10; cv = 0x102046600; sessiOnState= <_UICollectionViewDragSourceControllerSessionState:0x101985890 isActive = 1; dragSession = <_UIDragSessionImpl: 0x1121efff0>>; dragState = <_UICollectionViewDragSourceControllerDragState:0x107052760 dragFrom=(0,13) draggingItems=(0,13),(0,12),(0,17),(0,18),(0,19)>>; destination=<_UICollectionViewDragDestinationController:0x10199e420; sessiOnState= <_UIDragDestinationControllerSessionState: state = committing; isActive = 1; isDragging = 0; isReordering = 0>; reorderingState = <_UIDragDestinationControllerReorderingState: 0x10999a210>; dropProposalState = <_UIDragDestinationControllerDropProposalState: indexPath = (0,16); proposal = ; didDrop = 1; effectiveIndexPath = (0,16)>>>]

然后是几个取消的拖拽.

记录会话1-2.再次注意,draggingItems(8)的数量与cellAppearanceStatesByIndexPaths(8)中的条目数相匹配:

2018-02-20 14:25:31.132581+0000 Test[644:489734] performDropWithCoordinator: coordinator[<_UICollectionViewDragAndDropController:0x104e43640 -sessiOnKind= unsupported; reorderedItems = (
); shadowUpdates=(
); cellAppearanceStatesByIndexPaths={
    " {length = 2, path = 0 - 16}" = "<_UICollectionViewCellAppearanceState:0x11686d1d0 - dragState = dragging; appearance = ghosted>";
    " {length = 2, path = 0 - 18}" = "<_UICollectionViewCellAppearanceState:0x116d66810 - dragState = dragging; appearance = ghosted>";
    " {length = 2, path = 0 - 20}" = "<_UICollectionViewCellAppearanceState:0x116d62540 - dragState = dragging; appearance = ghosted>";
    " {length = 2, path = 0 - 22}" = "<_UICollectionViewCellAppearanceState:0x116d64d80 - dragState = dragging; appearance = ghosted>";
    " {length = 2, path = 0 - 17}" = "<_UICollectionViewCellAppearanceState:0x116d7e2a0 - dragState = dragging; appearance = ghosted>";
    " {length = 2, path = 0 - 19}" = "<_UICollectionViewCellAppearanceState:0x104e7a760 - dragState = dragging; appearance = ghosted>";
    " {length = 2, path = 0 - 21}" = "<_UICollectionViewCellAppearanceState:0x104e94fe0 - dragState = dragging; appearance = ghosted>";
    " {length = 2, path = 0 - 23}" = "<_UICollectionViewCellAppearanceState:0x11686efb0 - dragState = dragging; appearance = ghosted>";
};  cellAppearanceStatesByCellPointers=NSMapTable {
}
;  placeholderCOntexts= {(
)}; source=<_UICollectionViewDragSourceController:0x104e75d10; cv = 0x102046600; sessiOnState= <_UICollectionViewDragSourceControllerSessionState:0x104e9d6d0 isActive = 1; dragSession = <_UIDragSessionImpl: 0x1099b23a0>>; dragState = <_UICollectionViewDragSourceControllerDragState:0x1099b6770 dragFrom=(0,16) draggingItems=(0,16),(0,17),(0,18),(0,19),(0,23),(0,22),(0,21),(0,20)>>; destination=<_UICollectionViewDragDestinationController:0x10199e420; sessiOnState= <_UIDragDestinationControllerSessionState: state = committing; isActive = 1; isDragging = 0; isReordering = 0>; reorderingState = <_UIDragDestinationControllerReorderingState: 0x116d66a10>; dropProposalState = <_UIDragDestinationControllerDropProposalState: indexPath = (0,15); proposal = ; didDrop = 1; effectiveIndexPath = (0,15)>>>]

然后是几个被取消的拖拽.

记录会话1-3.这是覆盖细胞出现的时候.这里的关键是相信draggingItems(4)的数量与cellAppearanceStatesByIndexPaths(25)非常不同,后者包含许多未在该会话中拖动的索引路径:

2018-02-20 14:26:10.885854+0000 Test[644:489734] performDropWithCoordinator: coordinator[<_UICollectionViewDragAndDropController:0x104e43640 -sessiOnKind= unsupported; reorderedItems = (
); shadowUpdates=(
); cellAppearanceStatesByIndexPaths={
    " {length = 2, path = 0 - 30}" = "<_UICollectionViewCellAppearanceState:0x116d64c00 - dragState = dropped; appearance = normal>";
    " {length = 2, path = 0 - 31}" = "<_UICollectionViewCellAppearanceState:0x1168b8120 - dragState = dropped; appearance = normal>";
    " {length = 2, path = 0 - 32}" = "<_UICollectionViewCellAppearanceState:0x116d64c20 - dragState = dropped; appearance = normal>";
    " {length = 2, path = 0 - 33}" = "<_UICollectionViewCellAppearanceState:0x116dda1a0 - dragState = dropped; appearance = normal>";
    " {length = 2, path = 0 - 34}" = "<_UICollectionViewCellAppearanceState:0x116d7e340 - dragState = dropped; appearance = normal>";
    " {length = 2, path = 0 - 15}" = "<_UICollectionViewCellAppearanceState:0x116891a30 - dragState = dropped; appearance = normal>";
    " {length = 2, path = 0 - 16}" = "<_UICollectionViewCellAppearanceState:0x11686d1d0 - dragState = dropped; appearance = normal>";
    " {length = 2, path = 0 - 37}" = "<_UICollectionViewCellAppearanceState:0x116d9e300 - dragState = dropped; appearance = normal>";
    " {length = 2, path = 0 - 58}" = "<_UICollectionViewCellAppearanceState:0x11215b530 - dragState = dropped; appearance = normal>";
    " {length = 2, path = 0 - 38}" = "<_UICollectionViewCellAppearanceState:0x11689d650 - dragState = dropped; appearance = normal>";
    " {length = 2, path = 0 - 18}" = "<_UICollectionViewCellAppearanceState:0x116d66810 - dragState = dropped; appearance = normal>";
    " {length = 2, path = 0 - 39}" = "<_UICollectionViewCellAppearanceState:0x1168cfba0 - dragState = dropped; appearance = normal>";
    " {length = 2, path = 0 - 17}" = "<_UICollectionViewCellAppearanceState:0x116d7e2a0 - dragState = dropped; appearance = normal>";
    " {length = 2, path = 0 - 19}" = "<_UICollectionViewCellAppearanceState:0x104e7a760 - dragState = dropped; appearance = normal>";
    " {length = 2, path = 0 - 61}" = "<_UICollectionViewCellAppearanceState:0x11215e750 - dragState = dropped; appearance = normal>";
    " {length = 2, path = 0 - 20}" = "<_UICollectionViewCellAppearanceState:0x116d62540 - dragState = dropped; appearance = normal>";
    " {length = 2, path = 0 - 62}" = "<_UICollectionViewCellAppearanceState:0x1019f5ca0 - dragState = dropped; appearance = normal>";
    " {length = 2, path = 0 - 21}" = "<_UICollectionViewCellAppearanceState:0x104e94fe0 - dragState = dropped; appearance = normal>";
    " {length = 2, path = 0 - 22}" = "<_UICollectionViewCellAppearanceState:0x116d64d80 - dragState = dropped; appearance = normal>";
    " {length = 2, path = 0 - 41}" = "<_UICollectionViewCellAppearanceState:0x116d998c0 - dragState = dropped; appearance = normal>";
    " {length = 2, path = 0 - 23}" = "<_UICollectionViewCellAppearanceState:0x11686efb0 - dragState = dropped; appearance = normal>";
    " {length = 2, path = 0 - 44}" = "<_UICollectionViewCellAppearanceState:0x1121e6310 - dragState = dropped; appearance = normal>";
    " {length = 2, path = 0 - 45}" = "<_UICollectionViewCellAppearanceState:0x118e57190 - dragState = dropped; appearance = normal>";
    " {length = 2, path = 0 - 28}" = "<_UICollectionViewCellAppearanceState:0x116d65940 - dragState = dropped; appearance = normal>";
    " {length = 2, path = 0 - 29}" = "<_UICollectionViewCellAppearanceState:0x112144fc0 - dragState = dropped; appearance = normal>";
};  cellAppearanceStatesByCellPointers=NSMapTable {
}
;  placeholderCOntexts= {(
)}; source=<_UICollectionViewDragSourceController:0x104e75d10; cv = 0x102046600; sessiOnState= <_UICollectionViewDragSourceControllerSessionState:0x119620130 isActive = 1; dragSession = <_UIDragSessionImpl: 0x118e23a70>>; dragState = <_UICollectionViewDragSourceControllerDragState:0x119606080 dragFrom=(0,30) draggingItems=(0,30),(0,38),(0,33),(0,32)>>; destination=<_UICollectionViewDragDestinationController:0x10199e420; sessiOnState= <_UIDragDestinationControllerSessionState: state = committing; isActive = 1; isDragging = 0; isReordering = 0>; reorderingState = <_UIDragDestinationControllerReorderingState: 0x11939e0a0>; dropProposalState = <_UIDragDestinationControllerDropProposalState: indexPath = (0,66); proposal = ; didDrop = 1; effectiveIndexPath = (0,66)>>>]

根据行为和这些日志,我认为集合视图拖动跟踪的内部已经损坏.为什么是关键问题.

更新:此问题已在一个独立/最小的实现中成功复制,其中代码非常少:Test.zip

更新:这是一个视频,显示如何重现测试项目的问题:reproduction_issue.mp4


推荐阅读
  • 本文介绍了iOS数据库Sqlite的SQL语句分类和常见约束关键字。SQL语句分为DDL、DML和DQL三种类型,其中DDL语句用于定义、删除和修改数据表,关键字包括create、drop和alter。常见约束关键字包括if not exists、if exists、primary key、autoincrement、not null和default。此外,还介绍了常见的数据库数据类型,包括integer、text和real。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • Linux重启网络命令实例及关机和重启示例教程
    本文介绍了Linux系统中重启网络命令的实例,以及使用不同方式关机和重启系统的示例教程。包括使用图形界面和控制台访问系统的方法,以及使用shutdown命令进行系统关机和重启的句法和用法。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文介绍了使用PHP实现断点续传乱序合并文件的方法和源码。由于网络原因,文件需要分割成多个部分发送,因此无法按顺序接收。文章中提供了merge2.php的源码,通过使用shuffle函数打乱文件读取顺序,实现了乱序合并文件的功能。同时,还介绍了filesize、glob、unlink、fopen等相关函数的使用。阅读本文可以了解如何使用PHP实现断点续传乱序合并文件的具体步骤。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • 标题: ... [详细]
  • Oracle seg,V$TEMPSEG_USAGE与Oracle排序的关系及使用方法
    本文介绍了Oracle seg,V$TEMPSEG_USAGE与Oracle排序之间的关系,V$TEMPSEG_USAGE是V_$SORT_USAGE的同义词,通过查询dba_objects和dba_synonyms视图可以了解到它们的详细信息。同时,还探讨了V$TEMPSEG_USAGE的使用方法。 ... [详细]
  • 第四章高阶函数(参数传递、高阶函数、lambda表达式)(python进阶)的讲解和应用
    本文主要讲解了第四章高阶函数(参数传递、高阶函数、lambda表达式)的相关知识,包括函数参数传递机制和赋值机制、引用传递的概念和应用、默认参数的定义和使用等内容。同时介绍了高阶函数和lambda表达式的概念,并给出了一些实例代码进行演示。对于想要进一步提升python编程能力的读者来说,本文将是一个不错的学习资料。 ... [详细]
  • IOS开发之短信发送与拨打电话的方法详解
    本文详细介绍了在IOS开发中实现短信发送和拨打电话的两种方式,一种是使用系统底层发送,虽然无法自定义短信内容和返回原应用,但是简单方便;另一种是使用第三方框架发送,需要导入MessageUI头文件,并遵守MFMessageComposeViewControllerDelegate协议,可以实现自定义短信内容和返回原应用的功能。 ... [详细]
  • 使用圣杯布局模式实现网站首页的内容布局
    本文介绍了使用圣杯布局模式实现网站首页的内容布局的方法,包括HTML部分代码和实例。同时还提供了公司新闻、最新产品、关于我们、联系我们等页面的布局示例。商品展示区包括了车里子和农家生态土鸡蛋等产品的价格信息。 ... [详细]
  • 本文介绍了在iOS开发中使用UITextField实现字符限制的方法,包括利用代理方法和使用BNTextField-Limit库的实现策略。通过这些方法,开发者可以方便地限制UITextField的字符个数和输入规则。 ... [详细]
  • 上图是InnoDB存储引擎的结构。1、缓冲池InnoDB存储引擎是基于磁盘存储的,并将其中的记录按照页的方式进行管理。因此可以看作是基于磁盘的数据库系统。在数据库系统中,由于CPU速度 ... [详细]
author-avatar
mobiledu2502855653
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有