Вставить форматированный текст, а не изображения или HTML
Я пытаюсь эмулировать поведение монтажной панели приложений iOS Pages и Keynote. Короче говоря, позволяя вставлять базовое форматирование текста NSAttributedString (например, BIU) в UITextView, но не в изображения, HTML и т. Д.
РЕЗЮМЕ ПОВЕДЕНИЯ
- Если вы копируете отформатированный текст из приложения Notes, Evernote или текста и изображений с веб-сайта, Pages вставит только текстовую строку
- Если вы копируете форматированный текст из Pages или Keynote, он вставит форматированный текст в другие страницы в Pages, Keynote и т. Д.
- Нежелательное последствие, но, возможно, важно признать, это то, что ни приложение Notes, ни Evernote не будут вставлять форматированный текст, скопированный из Pages или Keynote. Я предполагаю, что несоответствие между приложениями заключается в использовании NSAttributedStrings, а не HTML?
Как это достигается? Похоже, что в Mac OS вы можете попросить монтажную панель возвращать различные типы, предоставить ей как расширенный текст, так и строковое представление, и использовать расширенный текст по своему усмотрению. К сожалению, readObjectsForClasses, похоже, не существует для iOS. Тем не менее, благодаря этому посту, я вижу по логу, что у iOS действительно есть тип монтажной платы, связанный с RTF. Однако я не могу найти способ запросить NSAttributedString версию содержимого монтажной панели, чтобы я мог расставить приоритеты для вставки.
ФОН
У меня есть приложение, которое позволяет базовое редактируемое пользователем форматирование NSAttributedString (то есть полужирный, курсив, подчеркивание) текста в UITextViews. Пользователи хотят копировать текст из других приложений (например, веб-страницу в Safari, текст в приложении Notes), чтобы вставить в UITextView в моем приложении. Разрешение работы с картоном по умолчанию означает, что я могу получить фоновые цвета, изображения, шрифты и т. Д., Которые мое приложение не предназначено для обработки. Пример ниже показывает текст, как выглядит скопированный текст с цветом фона при вставке в UITextView моего приложения.
Я могу преодолеть 1 путем создания подклассов UITextView
- (void)paste:(id)sender
{
UIPasteboard *pasteBoard = [UIPasteboard generalPasteboard];
NSString *string = pasteBoard.string;
NSLog(@"Pasteboard string: %@", string);
[self insertText:string];
}
Непреднамеренное последствие - потеря способности сохранять форматирование текста, скопированного из моего приложения. Пользователи могут захотеть скопировать текст из одного UITextView в моем приложении и вставить его в другой UITextView в моем приложении. Они ожидают, что форматирование (т.е. полужирный, курсив, подчеркивание) будет сохранено.
Проницательность и предложения приветствуются.
2 ответа
Несколько копий / вставок вкусностей и идей для вас:)
// Setup code in overridden UITextView.copy/paste
let pb = UIPasteboard.generalPasteboard()
let selectedRange = self.selectedRange
let selectedText = self.attributedText.attributedSubstringFromRange(selectedRange)
// UTI List
let utf8StringType = "public.utf8-plain-text"
let rtfdStringType = "com.apple.flat-rtfd"
let myType = "com.my-domain.my-type"
- Переопределите копию UITextView: и используйте ваш собственный тип монтажного стола
pb.setValue(selectedText.string, forPasteboardType: myType)
Чтобы разрешить копирование расширенного текста (в копии:):
// Try custom copy do { // Convert attributedString to rtfd data let fullRange = NSRange(location: 0, length: selectedText.string.characters.count) let data:NSData? = try selectedText.dataFromRange(fullRange, documentAttributes: [NSDocumentTypeDocumentAttribute: NSRTFDTextDocumentType]) if let data = data { // Set pasteboard values (rtfd and plain text fallback) pb.items = [[rtfdStringType: data], [utf8StringType: selectedText.string]] return } } catch { print("Couldn't copy") } // If custom copy not available; // Copy as usual super.copy(sender)
Чтобы разрешить вставку в расширенный текст (вставка:):
// Custom handling for rtfd type pasteboard data if let data = pb.dataForPasteboardType(rtfdStringType) { do { // Convert rtfd data to attributedString let attStr = try NSAttributedString(data: data, options: [NSDocumentTypeDocumentAttribute: NSRTFDTextDocumentType], documentAttributes: nil) // Bonus: Possibly strip all unwanted attributes here. // Insert it into textview replaceSelection(attStr) return } catch {print("Couldn't convert pasted rtfd")} } // Default handling otherwise (plain-text) else { super.paste(sender) }
Еще лучше, чем использовать пользовательский тип монтажной панели, занести в белый список все возможные теги, выполнить цикл и удалить все остальное при вставке.
- (Бонус: помогите UX в других приложениях, убрав ненужные атрибуты, которые вы добавили, при копировании (например, шрифт и fg-color))
Также стоит отметить, что textView может не захотеть вставлять, когда в монтажном столе содержится определенный тип, чтобы исправить это:
// Allow all sort of paste (might want to use a white list to check pb.items agains here) override func canPerformAction(action: Selector, withSender sender: AnyObject?) -> Bool { if action == #selector(UITextView.paste(_:)) { return true } return super.canPerformAction(action, withSender: sender) }
Кроме того, cut: может быть неплохо для реализации. В основном просто
copy:
а такжеreplaceSelection(emptyString)
Для вашего удобства:
// Helper to insert attributed text at current selection/cursor position func replaceSelection(attributedString: NSAttributedString) { var selectedRange = self.selectedRange let m = NSMutableAttributedString(attributedString: self.attributedText) m.replaceCharactersInRange(self.selectedRange, withAttributedString: attributedString) selectedRange.location += attributedString.string.characters.count selectedRange.length = 0 self.attributedText = m self.selectedRange = selectedRange }
Удачи!
Это должен быть комментарий к ответу Леонарда Паули, но у меня пока недостаточно репутации, чтобы комментировать.
Вместо:
selectedRange.location += attributedString.string.characters.count
(или attribuStString.string.count, как в более поздних версиях Swift)
Лучше всего использовать:
selectedRange.location += attributedString.length
В противном случае, когда вы вставите текст, содержащий эмодзи, которые приводят к различиям в атрибутах attribute.String.length и attribute.String.string.count, выбор окажется не в том месте.