iOS 11 Drag Drop使用(一)

章节

Drag & Drop除了基础APIs外,还为tableView,collectionView、textView提供了高级APIs,所以本系列分为以下几个部分:

  • 基础视图(本篇)
  • UITableView
  • UICollectionView
  • UISpringLoadedInteraction

概述

Drag & Drop是iOS 11中新增的API,允许App内或不同App之间通过手势Drag(拖拽)、Drop(投放)来交换数据。


iOS 11 Drag Drop使用(一)_第1张图片
1-1

由于iPad的分屏多任务支持,可以使用Drag、Drop的全部特性,iPhone只能在App内部使用。

API结构

iOS 11 Drag Drop使用(一)_第2张图片
Drag、Drop

基础部分:

  • UIDragInteraction、UIDropInteraction
  • UIDragSession、UIDropSession
  • UIDragPreview、UIDragPreviewParameters
  • UIDragItem、NSItemProvider

tableView:

  • UITableViewDragDelegate, UITableViewDropDelegate, UITableViewDropCoordinator, UITableViewDropItem, UITableViewDropPlaceholderContext

collectionView:

  • UICollectionViewDragDelegate, UICollectionViewDropDelegate, UICollectionViewDropCoordinator, UICollectionViewDropItem, UICollectionViewDropPlaceholderContext

示例

本篇将通过一个demo App与系统的照片App之间通过Drag、Drop来传递图片,解释部分基础API的使用。

  1. 首先,新建一个Xcode project,在ViewController中添加一个UIimageview,设置一个初始image。

2. 实现Drag功能
为了能够将UIimageview中的image拖拽到相册中,需要实现Drag功能,能够响应Drag手势。

2.1 确定Drag目标,本Demo中使用UIimageview。使用UIDragInteraction来实现,在viewDidLoad中添加

[self customEnableDraggingOnView:self.imageView dragInteractionDelegate:self];
/**
 add dragging support for specify view
 */
- (void)customEnableDraggingOnView:(UIView*)view dragInteractionDelegate:(id)delegate {
    if (!view) {
        return;
    }
    
    UIDragInteraction *dragInteraction = [[UIDragInteraction alloc] initWithDelegate:delegate];
    [view addInteraction:dragInteraction];
}

2.2 添加interaction后,当系统识别到针对imageView的Drag手势后,会调用UIDragInteractionDelegate的方法:

/*
* 需要在此回调中提供拖拽的数据封装,因为虽然拖拽的是imageView,但是实际上需要传递的数据应该为imageView中的image。
*/
- (NSArray *)dragInteraction:(UIDragInteraction *)interaction itemsForBeginningSession:(id)session {
    if (self.imageView.image == nil) {
        return nil;
    }
    
    /*
     * item provider为Drag、Drop之间传输数据
     * Drag、Drop支持的数据类型:
        CNContact
        CNMutableContact
        CSLocalizedString
        MKMapItem
        NSAttributedString
        NSMutableString
        NSString
        NSTextStorage
        NSURL
        UIColor
        UIImage
     */
    NSItemProvider *itemProvider = [[NSItemProvider alloc] initWithObject:self.imageView.image];
    UIDragItem *item = [[UIDragItem alloc] initWithItemProvider:itemProvider];
    
    return @[item];
}

2.3 在Drag开始后,如图1-1所示,手指处会显示preview预览效果,默认preview视图为Drag目标本身,本例中为imageView。如果需要自定义preview,则需要实现代理方法- (nullable UITargetedDragPreview *)dragInteraction:(UIDragInteraction *)interaction previewForLiftingItem:(UIDragItem *)item session:(id)session,那什么时候需要自定义preview呢?举个栗子,假如2.1中将Drag interaction添加到self.view上,那么默认Drag preview就是self.view,显然这并不符合要求,就需要将preview改成self.imageView。

- (nullable UITargetedDragPreview *)dragInteraction:(UIDragInteraction *)interaction previewForLiftingItem:(UIDragItem *)item session:(id)session {
    UIDragPreviewParameters *pama = [[UIDragPreviewParameters alloc] init];
    pama.visiblePath = [UIBezierPath bezierPathWithRect:self.imageView.frame];
    pama.backgroundColor = [UIColor redColor];
    UITargetedDragPreview *preview = [[UITargetedDragPreview alloc] initWithView:self.otherImageView parameters:pama];
    return preview;
    
//    return nil;
}

此处,已经实现了Drag,iPad模拟器运行,将demo与照片App分屏,按住imageView Drag到照片App里,松手Drop,可以发现imageView的image就保持到照片App里了。

3. 实现Drop功能
为了能够接受从其他App Drag到本App里的数据,需要使用Drop API。
3.1 确定接收数据的目标,本例为2.1中添加的imageView

[self customEnableDroppingOnView:self.imageView dragInteractionDelegate:self];
/**
 add dropping support for specify view
 */
- (void)customEnableDroppingOnView:(UIView*)view dragInteractionDelegate:(id)delegate {
    if (!view) {
        return;
    }
    
    UIDropInteraction *dropInteraction = [[UIDropInteraction alloc] initWithDelegate:delegate];
    [view addInteraction:dropInteraction];
}

3.2 当Drag目标到Drop interaction所附着的view坐标范围内,会调用- (BOOL)dropInteraction:(UIDropInteraction *)interaction canHandleSession:(id)session方法:

/*
 * 此方法决定是否响应Drop session
 * 本例中只接受单个的image Drop
 */
- (BOOL)dropInteraction:(UIDropInteraction *)interaction canHandleSession:(id)session {
    NSLog(@"---->>>>>>> canHandleSession");
    // only support image
    return [session hasItemsConformingToTypeIdentifiers:@[(__bridge_transfer NSString*)kUTTypeImage]] && session.items.count==1;
}

3.3 定义数据传输方式

/*
 当
 - (BOOL)dropInteraction:(UIDropInteraction *)interaction canHandleSession:(id)session
 返回YES后,就会调用此代理,
 UIDropProposal决定数据传输的方式,一般来说,
 1️⃣ 外部App drag到本App,使用copy来复制数据;
 2️⃣ App内部drag时,只移动数据
 */
- (UIDropProposal *)dropInteraction:(UIDropInteraction *)interaction sessionDidUpdate:(id)session {
    // localDragSession 不为nil时,表示App内部Drag、Drop
    if (session.localDragSession != nil) {
        //NSLog(@"---->>>>>>> UIDropOperationMove");
        return [[UIDropProposal alloc] initWithDropOperation:UIDropOperationMove];
    }
    // 外部Drag 目标
    else {
        CGPoint loc = [session locationInView:self.view];
        if (CGRectContainsPoint(self.imageView.frame, loc)) {
            //NSLog(@"---->>>>>>> UIDropOperationCopy");
            return [[UIDropProposal alloc] initWithDropOperation:UIDropOperationCopy];
        }
        else {
            return [[UIDropProposal alloc] initWithDropOperation:UIDropOperationForbidden];
        }
    }
    
    return [[UIDropProposal alloc] initWithDropOperation:UIDropOperationCancel];
}

3.4 Drop发生时

/*
 在Drop interaction的View上Drop内容时调用,具体数据的数据、UI表现在此方法中定义。
 */
- (void)dropInteraction:(UIDropInteraction *)interaction performDrop:(id)session {
    // App内Drag、Drop,只移动imageView的位置
    if (session.localDragSession != nil) {
        //self.imageView.center = [session locationInView:self.view];
    }
    else {
        // dragItem来自外部App时,需要加载具体的数据
        // 本例中即为从照片App中拷贝Drag图片到本App,然后使用imageView显示
        for (UIDragItem *item in session.items) {
            __weak __typeof(self) weakSelf = self;
            [item.itemProvider loadObjectOfClass:[UIImage class] completionHandler:^(id  _Nullable object, NSError * _Nullable error) {
                if (error) {
                    NSLog(@"----->>>>>> load data err: %@", [error localizedDescription]);
                    return ;
                }
                
                if (!object) {
                    return;
                }
                
                dispatch_async(dispatch_get_main_queue(), ^{
                    __strong __typeof(self) self = weakSelf;
                    self.imageView.image = (UIImage*)object;
                });
            }];
        }
    }
}

3.5 Drop功能完成,运行App,从照片App Drag图片到imageView上,然后Drop图片,可以发现,imageView就会显示所Drag的图片了。

Next:

  • Drag and Drop with Collection and Table View

参考资料

文档

  • Drag and Drop

视频

  • Introducing Drag and Drop
  • Mastering Drag and Drop
  • Drag and Drop with Collection and Table View
  • Data Delivery with Drag and Drop

代码

  • Adopting Drag and Drop in a Custom View
  • Adopting Drag and Drop in a Table View

Tips

文章难免会有错误、理解错位的地方,请不吝指教。

你可能感兴趣的:(iOS 11 Drag Drop使用(一))