Paste Formatted Text, Not Images or HTML

亡梦爱人 提交于 2019-11-28 13:54:27

This should be a comment on Leonard Pauli's answer, but I don't have enough reputation to make comments yet.

Instead of:

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

(or attributedString.string.count as it is in more recent versions of Swift)

It's best to use:

selectedRange.location += attributedString.length

Otherwise when you paste text that contains emoji that cause attributedString.length and attributedString.string.count to differ, the selection will end up in the wrong place.

Some copy/paste goodies and ideas, for you :)

// 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"
  • Override UITextView copy: and use your custom pasteboard type pb.setValue(selectedText.string, forPasteboardType: myType)
  • To allow rich text copy (in copy:):

    // 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)
    
  • To allow rich text paste (in paste:):

    // 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) }
    
  • Even better then using a custom pasteboard type, white-list all possibly wanted tags, loop through and strip away all other on paste.

  • (Bonus: help UX in other apps by stripping away unnecessary attributes you've added, on copy (like font and fg-color))
  • Also worth noting, the textView might not want to allow pasting when the pasteboard contains a specific type, to fix that:

    // 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)
    }
    
  • Furthermore, cut: could be nice to implement as well. Basically just copy: and replaceSelection(emptyString)

  • For your convenience:

    // 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
    }
    

Good luck!

Refs: Uniform Type Identifiers Reference

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!