DragAndDropKit-iOS15下一行代码集成跨应用间拖拽传递数据

前言

前段时间苹果刚推出了iOS15正式版,我也是第一时间就升级了体验。期间体验到了一个非常有趣的交互如下视频。

[图片上传失败...(image-6e15f5-1641800973359)]

如视频所示,iOS15下的苹果系统相册支持将图片、视频,文本等直接拖拽复制到其他应用(目前亲测自带备忘录、腾讯QQ等是支持的)。作为一个iOS工程师,第一时间对此产生了浓厚的兴趣,第一时间查阅了官方文档,寻找实现方案,并以官方Api为基础,设计了DragAndDropKit,采用Swift编写,自带Drop、Drag命名空间,支持链式语法,原则上两行代码就可以让您项目支持拖拽资源分享到其他应用。

支持版本

原则上支持iPad OS 11 + , iPhone 是 iOS15+才支持。

参考文档

drag and drop

下载地址

cocoaPods:


pod 'DragAndDropKit', '0.1.0'

github:

https://github.com/JerryFans/DragAndDropKit

DragAndDropKit演示与用法

[图片上传失败...(image-49125d-1641800973359)]

Drag

拖拽本应用的data到其他实现了Drop协议的应用(亲测支持系统相册、备忘录、网页里面的文本选中范围后直接拖拽等) DragAndDropKit本组件目前支持拖拽UIImage、本地视频(路径下的视频)、网络图片、网络视频、文本等。目前支持UIView及其类UIImageView、UILabel等、以及TableView、CollectionView快速拖动其子Cell。

UIView Drag Usage

最快只需两行代码即可实现拖拽,支持链式写法


/*
             你想拖拽时候给予的souce, 目前支持NetworkImageDropSource、NetworkVideoDropSource、ImageDropSource、VideoDropSource、TextDropSource五种
             */
            self.networkImageView.drag.dropSource = NetworkImageDropSource(imageUrl: "http://image.jerryfans.com/sample.jpg")
            
            //开启拖拽
            self.networkImageView.drag.enabled()

以及可选协议转链式闭包

self.networkImageView.drag.enabled().didEndSession { interaction, session, operation in
                
            }.didMoveSession { interaction, session in
                
            }.didPreviewForDragSession { interaction, item, session in
                return
            }.didAllowsMoveOperationSession { interaction, session in
                return false
            }

UICollectionView & UITableView Drag Usage

UICollectionView、UITableView因为实习的协议不同,但本质写法是差不多的。
两者enabled后都必须至少实现tableViewDidItemsForBeginning 或 collectionViewDidItemsForBeginning 闭包,返回相应的DragItem。DragItem封装的也是上面所说的5种DropSource。

tableView


tableView.drag.enabled().tableViewDidItemsForBeginning { [weak self] tableView, session, indexPath in
                guard let self = self else { return [] }
                //if you are the custom model, you should convert to DropSource Object (Text,Image or Video Drop Source)
                let source = self.models[indexPath.row]
                let itemProvider = NSItemProvider(object: source)
                return [
                    UIDragItem(itemProvider: itemProvider)
                ]
            }

collectionView,如果要实现一些生命周期方法,可以实现一下生命周期闭包,同样是链式语法。

collectionView.drag.enabled()
            .collectionViewDidItemsForBeginning { [weak self] collectionView, session, indexPath in
                return self?.dragAndDropVM.dragItems(for: indexPath) ?? []
            }.collectionViewWillBeginDragSession { collectionView, session in
                JFPopup.toast(hit: "collection view will begin drag")
            }.collectionViewDidEndDragSession { collectionView, session in
                JFPopup.toast(hit: "collection view did end drag")
            }

[图片上传失败...(image-cae300-1641800973359)] |
image

Drop

从其他应用的接收data到到本应用(亲测支持系统相册、备忘录、QQ发送聊天等) DragAndDropKit本组件目前支持Drop 接收 UIImage、本地视频(路径下的视频)、网络图片、网络视频、文本等。目前也是支持UIView及其类UIImageView、UILabel等、以及TableView、CollectionView快速拖动其子Cell。

Usage

支持类型参数,所有UIView类别、TableView、CollectionView、均可赋值supportSources,用来声明drop接收时候能支持的类型数据,默认全部支持(Image、Video、Text)三种。(注:并不是系统api只支持这三种,是这三种比较广泛,第一期先支持此三种数据的接收)


c.drop.supportSources = [.rawImage,.rawVideo,.text]

UIView Drop, didReceivedDropSource闭包必须实现用以接收到source后你对source的处理,其他可选。


self.view.drop.supportSources = [.rawImage]
            self.view.drop.enabled().didReceivedDropSource { [weak self] dropSources in
                for (_, item) in dropSources.enumerated() {
                    if let imageSource = item as? ImageDropSource {
                        self?.imageView.image = imageSource.image
                        self?.imageView.layer.borderWidth = 0.0
                        break
                    }
                }
            }.didEnterDropSession { interaction, session in
                if session.localDragSession == nil {
                    JFPopupView.popup.toast {
                        [.hit("请移入右上角图片中替换"),
                         .withoutAnimation(true),
                         .position(.top),
                         .autoDismissDuration(.seconds(value: 3)),
                         .bgColor(UIColor.jf.rgb(0x000000, alpha: 0.3))
                        ]
                    }
                }
            }.didUpdateDropSource { [weak self] interaction, session in
                guard let self = self else {
                    return UIDropProposal(operation: UIDropOperation.cancel)
                }
                let dropLocation = session.location(in: self.view)
                
                let operation: UIDropOperation
                
                if self.imageView.frame.contains(dropLocation) {
                    operation = session.localDragSession == nil ? .copy : .move
                    self.checkIsMatch(match: true)
                } else {
                    operation = .cancel
                    self.checkIsMatch(match: false)
                }
                self.updateLayers(forDropLocation: dropLocation)
                
                return UIDropProposal(operation: operation)
            }.didEndDropSession { [weak self] interaction, session in
                guard let self = self else { return }
                let dropLocation = session.location(in: self.view)
                self.updateLayers(forDropLocation: dropLocation)
                self.checkIsMatch(match: false)
            }.didExitDropSession { [weak self] interaction, session in
                guard let self = self else { return }
                self.imageView.layer.borderWidth = 0.0
            }
        }

UICollectionView类似,也是collectionViewDidReceivedDropSource必须处理,其他生命周期闭包,可选。


c.drop.supportSources = [.rawImage,.rawVideo,.text]
            c.drop.enabled().collectionViewDidReceivedDropSource { [weak self] collectionView, coordinator, dropSources in
                let destinationIndexPath: IndexPath
                
                if let indexPath = coordinator.destinationIndexPath {
                    destinationIndexPath = indexPath
                } else {
                    let item = collectionView.numberOfItems(inSection: 0)
                    destinationIndexPath = IndexPath(item: item, section: 0)
                }
                var indexPaths = [IndexPath]()
                for (index, item) in dropSources.enumerated() {
                    let indexPath = IndexPath(item: destinationIndexPath.item + index, section: destinationIndexPath.section)
                    self?.dragAndDropVM.addItem(item, at: indexPath.item)
                    indexPaths.append(indexPath)
                }
                self?.collectionView.insertItems(at: indexPaths)
            }

UITableView类似,也是tableViewDidReceivedDropSource必须处理,其他生命周期闭包,可选。


t.drop.supportSources = [.rawImage,.rawVideo,.text]
            t.drop.enabled().tableViewDidReceivedDropSource { [weak self] tableView, coordinator, dropSources in
                guard let self = self else { return }
                
                let destinationIndexPath: IndexPath
                
                if let indexPath = coordinator.destinationIndexPath {
                    destinationIndexPath = indexPath
                } else {
                    let item = tableView.numberOfRows(inSection: 0)
                    destinationIndexPath = IndexPath(row: item, section: 0)
                }
                var indexPaths = [IndexPath]()
                for (index, item) in dropSources.enumerated() {
                    let indexPath = IndexPath(row: destinationIndexPath.item + index, section: destinationIndexPath.section)
                    self.models.insert(item, at: indexPath.row)
                    indexPaths.append(indexPath)
                }
                tableView.insertRows(at: indexPaths, with: .bottom)
            }

效果:

[图片上传失败...(image-84abfd-1641800973359)]

后续支持

  • tableView & collectionView 和系统相册一样支持多选拖拽支持(目前只能一个个拖cell)
  • 更多DropSource的支持
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容

  • 用到的组件 1、通过CocoaPods安装 2、第三方类库安装 3、第三方服务 友盟社会化分享组件 友盟用户反馈 ...
    SunnyLeong阅读 14,598评论 1 180
  • ![Flask](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAW...
    极客学院Wiki阅读 7,229评论 0 3
  • 不知不觉易趣客已经在路上走了快一年了,感觉也该让更多朋友认识知道易趣客,所以就谢了这篇简介,已做创业记事。 易趣客...
    Physher阅读 3,407评论 1 2
  • 双胎妊娠有家族遗传倾向,随母系遗传。有研究表明,如果孕妇本人是双胎之一,她生双胎的机率为1/58;若孕妇的父亲或母...
    邺水芙蓉hibiscus阅读 3,695评论 0 2
  • 今天理好了行李,看到快要九点了,就很匆忙的洗头洗澡,(心存一份念想,你总会打给我的🐶)然后把洗头液当成沐浴液了😨,...
    bevil阅读 2,767评论 1 1