以下內容都是自己閱讀實踐所得,如有誤請多指教,謝謝。
(示例代碼為Swift,剛學,請多指教)
Drag and Drop 簡單介紹
Drag and Drop是iOS11的一套基于UIKit的新API。它可以讓你拖動屏幕上的視圖進行一些操作,包括但不限于移動,復制內容。甚至可以在不同App之間進行Drag and drop,就像WWDC上面演示的一樣: Introducing Drag and Drop,但是在iPhone上,Drag and Drop僅支持單App。
接下來我將通過幾個Demo來講解如何去簡單開發Drag and Drop的應用。
今天這篇文章將講解如何使用UIView的新屬性pasteConfiguration去最簡單的實現一個拖動進行粘貼的操作,如圖:
Step1 給View添加Drag
UIDragInteraction & UIDropInteraction
這兩個可以說是Drag'n Drop 開發中最基本的類,都繼承自UIInteraction,屬性上也基本相同。
你可以把他們當成UIGestureRecognizer,使用的時候只需要初始化一個UIDragInteraction/UIDropInteraction,設定好delegate,再添加到相應的view即可:
//Add an UIDragInteraction to support drag
dragImageView.addInteraction(UIDragInteraction.init(delegate: self))
此時你的view就可以支持長按進行拖動了(但是在iPhone上同樣的代碼卻不起作用,不知道為什么,明白的小伙伴歡迎解答)
注意使用的時候需要將view的isUserInteractionEnabled設為true
Step2 給目的地上的View添加pasteConfiguration
在本例中我們創建兩個UIImageView,一個用來展示原始圖片并提供拖動(dragImageView),拖到另一個上(pasteImageView)后,進行粘貼的操作。
這里只需要在dragImageView上添加UIDragInteraction即可,要讓pasteImageView支持Drop的話,上文已經說到,最簡單的方法就是配置pasteConfiguration屬性:
pasteImageView.pasteConfiguration = UIPasteConfiguration(forAccepting: UIImage.self)
以上代碼就是聲明pasteImageView支持drop功能,并且你可以把包含UIImage數據的對象拖到這個pasteImageView上來進行粘貼。當然通過配置UIPasteConfiguration,可以支持很多不同的數據類型。
同時必須重寫改view的paste方法,下文詳說
Step3 實現UIDragInteraction
這時運行demo會發現即時我們在圖片上按再長的時間,也沒有任何變化,也不能拖動。
因為我們必須提供一個被拖動的對象,當然是數據層面上的,用來告訴系統我們正在拖text呢 還是image呢,以便其他支持Drop的view可以根據數據類型來決定是否能夠接收drop。
func dragInteraction(_ interaction: UIDragInteraction, itemsForBeginning session: UIDragSession) -> [UIDragItem] {
if interaction.view == dragImageView {
let dragImage = dragImageView.image
let itemProvider = NSItemProvider.init(object: dragImage!)
let dragItem = UIDragItem.init(itemProvider: itemProvider)
return [dragItem]
}
else if interaction.view == dragTextLabel {
let dragText = "Try drag me~"
let itemProvider = NSItemProvider.init(object: dragText as NSString)
let dragItem = UIDragItem.init(itemProvider: itemProvider)
return [dragItem]
}
else {
return []
}
}
這里涉及到兩個新類,UIDragItem & NSItemProvider
NSItemProvider簡單來說就是用來在Drag and Drop,或者Extension app 和Host app之間傳輸數據的類。
UIDragItem則是像對NSItemProvider的進一步封裝,除了包含傳輸數據外,還可以自定義一些數據。
具體用法就是如上:
let dragImage = dragImageView.image
let itemProvider = NSItemProvider.init(object: dragImage!)
let dragItem = UIDragItem.init(itemProvider: itemProvider)
return [dragItem]
將我們要拖動的Image對象一層層包起來返回即可。
這時再運行demo就發現可以拖動了,而且當圖片懸浮在pasteImageView上時可以看到右上角有加號,說明此處支持粘貼的操作,但是鼠標一松開就Crash了。
Step4 重寫Paste
上面的Crash信息顯示:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'pasteItemProviders: must be overridden if pasteConfiguration is not nil.'
上文就說必須重寫pasteItemProviders。
那我們重寫一下pasteImageView的pasteItemProviders方法,讓它支持粘貼。
override func paste(itemProviders: [NSItemProvider]) {
for dragItem in itemProviders {
if dragItem.canLoadObject(ofClass: UIImage.self) {
dragItem.loadObject(ofClass: UIImage.self, completionHandler: { (image, error) in
if image != nil {
DispatchQueue.main.async {
self.tipLayer.removeFromSuperlayer()
self.customBorderLayer.removeFromSuperlayer()
self.image = (image as! UIImage)
}
}
})
}
}
}
以上操作就是先判斷這個NSItemProvider是否可以load UIImage這一類的數據,如果可以我們就load出來進行界面更新等操作。
這時再運行一遍應該就可以復制了。
-----
以上全部就是比較詳細的介紹了如何使用view的pasteConfiguration來簡單實現Drag to paste,留下一個拖動文字的功能可以自己嘗試實現一下。
小提示:textField不用配置pasteConfiguration,自己對于NSString類型的數據有默認實現。