| ... | ... | @@ -141,7 +141,7 @@ private final class ComposerTextView: UITextView { |
| 141 | 141 | replace(textRange, withText: "") |
| 142 | 142 | } |
| 143 | 143 | |
| 144 | | private func loadImages(from providers: [NSItemProvider]) { |
| 144 | func loadImages(from providers: [NSItemProvider]) { |
| 145 | 145 | let group = DispatchGroup() |
| 146 | 146 | let lock = NSLock() |
| 147 | 147 | var images = [UIImage?](repeating: nil, count: providers.count) |
| ... | ... | @@ -282,7 +282,7 @@ private final class ComposerTextView: UITextView { |
| 282 | 282 | } |
| 283 | 283 | } |
| 284 | 284 | |
| 285 | | public final class T3ComposerEditorView: ExpoView, UITextViewDelegate { |
| 285 | public final class T3ComposerEditorView: ExpoView, UITextViewDelegate, UITextDropDelegate { |
| 286 | 286 | private let textView = ComposerTextView() |
| 287 | 287 | private let placeholderLabel = UILabel() |
| 288 | 288 | private var value = "" |
| ... | ... | @@ -326,6 +326,7 @@ public final class T3ComposerEditorView: ExpoView, UITextViewDelegate { |
| 326 | 326 | |
| 327 | 327 | clipsToBounds = false |
| 328 | 328 | textView.delegate = self |
| 329 | textView.textDropDelegate = self |
| 329 | 330 | textView.backgroundColor = .clear |
| 330 | 331 | textView.textContainerInset = .zero |
| 331 | 332 | textView.textContainer.lineFragmentPadding = 0 |
| ... | ... | @@ -500,6 +501,41 @@ public final class T3ComposerEditorView: ExpoView, UITextViewDelegate { |
| 500 | 501 | return true |
| 501 | 502 | } |
| 502 | 503 | |
| 504 | public func textDroppableView( |
| 505 | _ textDroppableView: UIView & UITextDroppable, |
| 506 | proposalForDrop drop: UITextDropRequest |
| 507 | ) -> UITextDropProposal { |
| 508 | guard droppedImageProviders(in: drop) != nil else { |
| 509 | return drop.suggestedProposal |
| 510 | } |
| 511 | |
| 512 | // The composer owns image drops so UIKit does not insert NSTextAttachments |
| 513 | // that the controlled plain-text value cannot represent. |
| 514 | let proposal = UITextDropProposal(operation: .copy) |
| 515 | proposal.dropAction = .insert |
| 516 | proposal.dropPerformer = .delegate |
| 517 | return proposal |
| 518 | } |
| 519 | |
| 520 | public func textDroppableView( |
| 521 | _ textDroppableView: UIView & UITextDroppable, |
| 522 | willPerformDrop drop: UITextDropRequest |
| 523 | ) { |
| 524 | guard let imageProviders = droppedImageProviders(in: drop) else { |
| 525 | return |
| 526 | } |
| 527 | textView.loadImages(from: imageProviders) |
| 528 | } |
| 529 | |
| 530 | private func droppedImageProviders(in drop: UITextDropRequest) -> [NSItemProvider]? { |
| 531 | let providers = drop.dropSession.items.map(\.itemProvider) |
| 532 | guard !providers.isEmpty, |
| 533 | providers.allSatisfy({ $0.canLoadObject(ofClass: UIImage.self) }) else { |
| 534 | return nil |
| 535 | } |
| 536 | return providers |
| 537 | } |
| 538 | |
| 503 | 539 | public func textViewDidBeginEditing(_ textView: UITextView) { |
| 504 | 540 | onComposerFocus() |
| 505 | 541 | } |