I'm implementing drag and drop in my app.
One of the main screens where drag and drop will be applied (mostly drop) is in a UITextView
.
I've added a drop interaction to this UITextView
with the following code:
let dropInteraction = UIDropInteraction(delegate: self)
inputTextView.addInteraction(dropInteraction)
By doing so I now accept drops in it.
In order to accept only images and text as drop I've implemented the following delegate method:
func dropInteraction(_ interaction: UIDropInteraction, canHandle session: UIDropSession) -> Bool {
return session.hasItemsConforming(toTypeIdentifiers: [kUTTypeImage as String, kUTTypeText as String]) && session.items.count == 1
}
As required I've implemented as well the following required delegate method to return a UIDropProposal
:
func dropInteraction(_ interaction: UIDropInteraction, sessionDidUpdate session: UIDropSession) -> UIDropProposal {
let dropProposal: UIDropProposal = UIDropProposal(operation: .copy)
dropProposal.isPrecise = true
return dropProposal
}
Then in the performDrop delegate method I handle the dropped content and attach it to the UITextView
func dropInteraction(_ interaction: UIDropInteraction, performDrop session: UIDropSession) {
// Access the dropped object types: UIImage, NSString
session.loadObjects(ofClass: UIImage.self) { (imageItems) in
let images = imageItems as! [UIImage]
guard var image = images.first else { return }
// scale the image
image = UIImage(cgImage: image.cgImage!, scale: 4, orientation: .up)
let currentText: NSMutableAttributedString = NSMutableAttributedString(attributedString: self.inputTextView.attributedText)
let textAttachment = NSTextAttachment()
textAttachment.image = image
let imageText = NSAttributedString(attachment: textAttachment).mutableCopy() as! NSMutableAttributedString
let imageStyle = NSMutableParagraphStyle()
imageStyle.alignment = .center
imageText.addAttribute(NSAttributedStringKey.paragraphStyle, value: imageStyle, range: NSMakeRange(0, imageText.length))
// Prepares the string to append with line breaks and the image centered
let attributedStringToAppend: NSMutableAttributedString = NSMutableAttributedString(attributedString: NSAttributedString(string: "\n\n"))
attributedStringToAppend.append(imageText)
attributedStringToAppend.append(NSAttributedString(string: "\n\n"))
// Applies text attributes to the NSMutableAttrString as the default text input attributes
let range: NSRange = NSRange.init(location: 0, length: attributedStringToAppend.length)
let style = NSMutableParagraphStyle()
style.lineSpacing = 4
style.alignment = .center
let font = Font(.installed(.OpenSansRegular), size: .custom(18)).instance
let attr: [NSAttributedStringKey : Any] = [NSAttributedStringKey(rawValue: NSAttributedStringKey.paragraphStyle.rawValue): style, NSAttributedStringKey(rawValue: NSAttributedStringKey.font.rawValue): font, NSAttributedStringKey(rawValue: NSAttributedStringKey.foregroundColor.rawValue): UIColor.rgb(red: 52, green: 52, blue: 52)]
attributedStringToAppend.addAttributes(attr, range: range)
let location = self.inputTextView.selectedRange.location
currentText.insert(attributedStringToAppend, at: location)
self.inputTextView.attributedText = currentText
}
session.loadObjects(ofClass: NSString.self) { (stringItems) in
let strings = stringItems as! [NSString]
guard let text = strings.first else { return }
self.inputTextView.insertText(text as String)
}
}
By doing all of this both images and text are dropped as expected into the desired field but I have one problem here.
In the New Mail Composer
in the mail app, when you're dragging either items or images to it, it shows a tracking cursor so you know where the drop content goes to, but I cannot do this in my example.
The drops goes to the end of the existing text.
If I want to drop to a specific location, I have to before place the cursor in the desired position and then perform the drop, so in the performDrop
function as you see, I retrieve the cursor location by the selectedRange
.
I want to see the cursor flow as the drop animation is occurring, just like the mail app or the notes app, where you can choose where the drop object goes to.
Does anyone have a hint on how to achieve this?
Thanks in advance.