美文网首页
iOS 11 Drag Drop使用(一)

iOS 11 Drag Drop使用(一)

作者: 0xfb | 来源:发表于2017-10-29 15:48 被阅读0次

    章节

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

    概述

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


    1-1

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

    API结构

    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<UIDragInteractionDelegate>)delegate {
        if (!view) {
            return;
        }
        
        UIDragInteraction *dragInteraction = [[UIDragInteraction alloc] initWithDelegate:delegate];
        [view addInteraction:dragInteraction];
    }
    

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

    /*
    * 需要在此回调中提供拖拽的数据封装,因为虽然拖拽的是imageView,但是实际上需要传递的数据应该为imageView中的image。
    */
    - (NSArray<UIDragItem *> *)dragInteraction:(UIDragInteraction *)interaction itemsForBeginningSession:(id<UIDragSession>)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<UIDragSession>)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<UIDragSession>)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<UIDropInteractionDelegate>)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<UIDropSession>)session方法:

    /*
     * 此方法决定是否响应Drop session
     * 本例中只接受单个的image Drop
     */
    - (BOOL)dropInteraction:(UIDropInteraction *)interaction canHandleSession:(id<UIDropSession>)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<UIDropSession>)session
     返回YES后,就会调用此代理,
     UIDropProposal决定数据传输的方式,一般来说,
     1️⃣ 外部App drag到本App,使用copy来复制数据;
     2️⃣ App内部drag时,只移动数据
     */
    - (UIDropProposal *)dropInteraction:(UIDropInteraction *)interaction sessionDidUpdate:(id<UIDropSession>)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<UIDropSession>)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<NSItemProviderReading>  _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

    参考资料

    文档

    视频

    代码

    Tips

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

    相关文章

      网友评论

          本文标题:iOS 11 Drag Drop使用(一)

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