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

3,792 阅读4分钟

前言

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

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

支持版本

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

参考文档

drag and drop

下载地址

cocoaPods:


pod 'DragAndDropKit', '0.2.0'

github:

github.com/JerryFans/D…

DragAndDropKit演示与用法

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")

}

|

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)

}

效果:

后续支持

  • tableView & collectionView 和系统相册一样支持多选拖拽支持(目前只能一个个拖cell)

  • 更多DropSource的支持