- [UIDragSession loadObjectsOfClass:completion:] completionBlock未被调用

时间:2017-12-19 04:28:03

标签: ios objective-c

我正在学习iOS11新API拖放,但我有一些问题。 我有两个collectionViews,它们共享相同类型( DataEntity )的dataSource数组。一个是拖动,另一个是拖放。这意味着我想将包含数据( DataEntity )的项从一个collectionView拖到另一个。

然后我在[-(id<UICollectionDropDelegate>)collectionView:performDropWithCoordinator:]中提出了问题。我无法获取第一个collectionView中传递的数据( DataEntity ),因为未调用-[UIDragSession loadObjectsOfClass:completion:] completionBlock。但是,如果我将其他类(例如UIImage.class或NSString.class)设置为参数loadObjectsOfClass:将调用完成块,但它不是兼容类,因此没有返回对象。

源代码

拖动collectionView

- (NSArray<UIDragItem *> *)collectionView:(UICollectionView *)collectionView itemsForBeginningDragSession:(id<UIDragSession>)session atIndexPath:(NSIndexPath *)indexPath {

    DataEntity* entity = self.entities[indexPath.row];
    UIDragItem* item = [[UIDragItem alloc] initWithItemProvider:[[NSItemProvider alloc] initWithObject:entity]];
    return @[item];
}

删除collectionView

- (void)collectionView:(UICollectionView *)collectionView performDropWithCoordinator:(id<UICollectionViewDropCoordinator>)coordinator {
    NSIndexPath* destinationIndexPath = coordinator.destinationIndexPath;
    dispatch_async(dispatch_get_main_queue(), ^{
        [self.collectionView performBatchUpdates:^{
            [coordinator.session loadObjectsOfClass:DataEntity.class completion:^(NSArray<__kindof id<NSItemProviderReading>> * _Nonnull objects) {
                [objects enumerateObjectsUsingBlock:^(__kindof id<NSItemProviderReading>  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
                    [self.mutableEntities insertObject:(DataEntity*)obj atIndex:destinationIndexPath.row];
                    [self.collectionView insertItemsAtIndexPaths:@[destinationIndexPath]];
                }];
            }];
        } completion:nil];
    });
}
- (BOOL)collectionView:(UICollectionView *)collectionView canHandleDropSession:(id<UIDropSession>)session {
    BOOL test = [session canLoadObjectsOfClass:DataEntity.class]; // It's YES
    return test;
}

数据实体

- (NSProgress *)loadDataWithTypeIdentifier:(NSString *)typeIdentifier forItemProviderCompletionHandler:(void (^)(NSData * _Nullable, NSError * _Nullable))completionHandler {
    NSData* data = [NSKeyedArchiver archivedDataWithRootObject:self];
    completionHandler(data, nil);
    return nil;
}
+ (NSArray<NSString *> *)writableTypeIdentifiersForItemProvider {
    NSString* identifier = NSStringFromClass(self.class);
    return @[identifier];
}
+ (NSArray<NSString *> *)readableTypeIdentifiersForItemProvider {
    NSString* identifier = NSStringFromClass(self.class);
    return @[identifier];
}
+ (nullable instancetype)objectWithItemProviderData:(nonnull NSData *)data typeIdentifier:(nonnull NSString *)typeIdentifier error:(NSError * _Nullable __autoreleasing * _Nullable)outError {
    DataEntity* entity = [NSKeyedUnarchiver unarchiveObjectWithData:data];
    return entity;
}

修改

好的,我找到了一些东西。 首先,如果我删除dispatch_async(dispatch_get_main_queue()),我将收到错误无法识别的选择器 - [DataEntity encodeWithCoder:] 。这是第二件事,我忘了让 DataEntity 符合 NSCoding 协议。

现在新问题是,为什么我无法在 dispatch_main_queue 关闭中调用-[UIDragSession loadObjectsOfClass:completion:],或者其完成块不会被调用?

1 个答案:

答案 0 :(得分:1)

这一切都与异步代码执行的基本原理有关。在您的collectionView:performDropWithCoordinator:实现中,此代码错误:

dispatch_async(dispatch_get_main_queue(), ^{
    [coordinator.session loadObjectsOfClass: // ...

当您致电dispatch_async时,您允许拨打collectionView:performDropWithCoordinator:的电话 - 它即将结束!但是,在继续加载数据之前,您正在执行。因此,下降立即结束,会话消失,之前您有机会获取数据。当我们到达loadObjectsOfClass时,再也没有数据了;会议已经结束。

事实上,我打赌那时的sessionnil。发送到nil对象的代码什么都不做;这就是为什么你的objectWithItemProviderData和完成处理程序永远不会被调用的原因。