Вставьте форматированный текст, а не изображения или HTML

Я пытаюсь имитировать поведение монтажного стола приложений iOS Pages и Keynote. Короче говоря, позволяя вставлять базовое форматирование текста NSAttributedString (то есть BIU) в UITextView, но не изображения, HTML и т. Д.

РЕЗЮМЕ ПОВЕДЕНИЯ

  1. Если вы скопируете форматированный текст из приложения Notes, Evernote или текст и изображения с веб-сайта, Pages вставит только текстовую строку.
  2. Если вы скопируете форматированный текст из Pages или Keynote, он вставит форматированный текст в другое место в Pages, Keynote и т. Д.
  3. Нежелательным последствием, но, возможно, важным, что следует признать, является то, что ни приложение 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 в моем приложении. Они ожидают, что форматирование (т. Е. Жирный шрифт, курсив, подчеркивание) будет сохранено.

Понимание и предложения приветствуются.


person DenVog    schedule 04.01.2015    source источник
comment
Да, вероятно, разница между RTFD и HTML - вот в чем разница, когда дело доходит до стилей вставки между приложениями :)   -  person Leonard Pauli    schedule 05.07.2016


Ответы (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 в других приложениях, удалив ненужные атрибуты, которые вы добавили, при копировании (например, font и 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
    }
    

Удачи!

Ссылки: Справочник по унифицированным идентификаторам типов

person Leonard Pauli    schedule 05.07.2016

Это должен быть комментарий к ответу Леонарда Паули, но у меня пока недостаточно репутации, чтобы делать комментарии.

Вместо того:

selectedRange.location += attributedString.string.characters.count 

(или attributedString.string.count, как в более поздних версиях Swift)

Лучше всего использовать:

selectedRange.location += attributedString.length

В противном случае, когда вы вставляете текст, содержащий эмодзи, из-за которых атрибуты attributedString.length и attributedString.string.count различаются, выделенный фрагмент окажется в неправильном месте.

person Angela    schedule 24.06.2018