| ... | @@ -141,7 +141,7 @@ private final class ComposerTextView: UITextView { | ... | @@ -141,7 +141,7 @@ private final class ComposerTextView: UITextView { |
| 141 | replace(textRange, withText: "") | 141 | replace(textRange, withText: "") |
| 142 | } | 142 | } |
| 143 | | 143 | |
| 144 | private func loadImages(from providers: [NSItemProvider]) { | 144 | func loadImages(from providers: [NSItemProvider]) { |
| 145 | let group = DispatchGroup() | 145 | let group = DispatchGroup() |
| 146 | let lock = NSLock() | 146 | let lock = NSLock() |
| 147 | var images = [UIImage?](repeating: nil, count: providers.count) | 147 | var images = [UIImage?](repeating: nil, count: providers.count) |
| ... | @@ -282,7 +282,7 @@ private final class ComposerTextView: UITextView { | ... | @@ -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 | private let textView = ComposerTextView() | 286 | private let textView = ComposerTextView() |
| 287 | private let placeholderLabel = UILabel() | 287 | private let placeholderLabel = UILabel() |
| 288 | private var value = "" | 288 | private var value = "" |
| ... | @@ -326,6 +326,7 @@ public final class T3ComposerEditorView: ExpoView, UITextViewDelegate { | ... | @@ -326,6 +326,7 @@ public final class T3ComposerEditorView: ExpoView, UITextViewDelegate { |
| 326 | | 326 | |
| 327 | clipsToBounds = false | 327 | clipsToBounds = false |
| 328 | textView.delegate = self | 328 | textView.delegate = self |
| | 329 | textView.textDropDelegate = self |
| 329 | textView.backgroundColor = .clear | 330 | textView.backgroundColor = .clear |
| 330 | textView.textContainerInset = .zero | 331 | textView.textContainerInset = .zero |
| 331 | textView.textContainer.lineFragmentPadding = 0 | 332 | textView.textContainer.lineFragmentPadding = 0 |
| ... | @@ -500,6 +501,41 @@ public final class T3ComposerEditorView: ExpoView, UITextViewDelegate { | ... | @@ -500,6 +501,41 @@ public final class T3ComposerEditorView: ExpoView, UITextViewDelegate { |
| 500 | return true | 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 | public func textViewDidBeginEditing(_ textView: UITextView) { | 539 | public func textViewDidBeginEditing(_ textView: UITextView) { |
| 504 | onComposerFocus() | 540 | onComposerFocus() |
| 505 | } | 541 | } |