粘贴格式化文本,而不是图像或HTML

时间:2015-01-04 19:43:02

标签: ios ios7 copy-paste nsattributedstring uipasteboard

我正在尝试模拟iOS Pages和Keynote应用的粘贴板行为。简而言之,允许将基本的NSAttributedString文本格式(即BIU)粘贴到UITextView中,但不粘贴到图像,HTML等中。

行为摘要

  1. 如果您从Notes应用程序,Evernote或网站上的文本和图像复制格式化文本,则Pages将仅粘贴纯文本字符串
  2. 如果您从Pages或Keynote中复制格式化文本,它会将格式化文本粘贴到Pages,Keynote等中的其他位置。
  3. 一个不受欢迎的结果,但也许重要的是要承认,Notes应用程序或Evernote都不会粘贴从Pages或Keynote复制的格式化文本。我猜测应用程序之间的差异是使用NSAttributedStrings而不是HTML?
  4. 这是如何实现的?在Mac OS上,您似乎可以自己ask the pasteboard to return different types,让它同时提供富文本和字符串表示,并使用富文本作为首选。不幸的是,对于iOS,readObjectsForClasses似乎不存在。也就是说,我可以通过日志看到iOS确实有RTF相关类型的粘贴板,thanks to this post。但是,我无法找到一种方法来请求NSAttributedString版本的粘贴板内容,因此我可以优先考虑粘贴。

    背景

    我有一个应用程序,它允许UITextViews中基本的NSAttributedString用户可编辑格式(即粗体,斜体,下划线)。用户希望从其他应用程序(例如Safari中的网页,Notes应用程序中的文本)复制文本,以粘贴到我的应用程序中的UITextView中。允许粘贴板作为默认操作意味着我可能最终得到我的应用程序不打算处理的背景颜色,图像,字体等。下面的示例显示了粘贴到我的应用程序的UITextView时带有背景颜色的复制文本的外观。

    enter image description here

    我可以通过继承UITextView

    来克服1
    - (void)paste:(id)sender
    {
        UIPasteboard *pasteBoard = [UIPasteboard generalPasteboard];
        NSString *string = pasteBoard.string;
        NSLog(@"Pasteboard string: %@", string);
        [self insertText:string];
    }
    

    意想不到的后果是,无法保留从我的应用中复制的文本格式。用户可能希望从我的应用程序中的一个UITextView复制文本,并将其粘贴到我的应用程序中的另一个UITextView。他们希望保留格式(即粗体,斜体,下划线)。

    洞察力和建议表示赞赏。

2 个答案:

答案 0 :(得分:3)

这应该是对伦纳德·保利(Leonard Pauli)答案的评论,但是我没有足够的声誉来发表评论。

代替:

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

(或attributedString.string.count,如在较新版本的Swift中一样)

最好使用:

selectedRange.location += attributedString.length

否则,当您粘贴包含表情符号的文本时,这些表情符号会导致attributedString.length和attributedString.string.count不同,那么选择的结果将放在错误的位置。

答案 1 :(得分: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) }
    
  • 更好的是使用自定义粘贴板类型,白名单所有可能需要的标签,循环并剥离所有其他粘贴。

  • (奖励:通过剥离您在复制时添加的不必要的属性(如字体和fg颜色)来帮助其他应用中的用户体验)
  • 另外值得注意的是,当粘贴板包含特定类型时,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
    }
    
祝你好运!

参考文献: Uniform Type Identifiers Reference