Copy NSAttributedString in UIPasteBoard

How do you copy an NSAttributedString in the pasteboard, to allow the user to paste, or to paste programmatically (with - (void)paste:(id)sender, from UIResponderStandardEditActions protocol).

I tried:

  • How to clear/empty pasteboard on viewWillDisappear
  • Pasteboard UIImage not using scale
  • copy HTML to UIPasteboard
  • Paste Formatted Text, Not Images or HTML
  • How to write custom metadata to PNG images in iOS
  • Copy Image with UIPasteBoard (Swift)
  • UIPasteboard *pasteBoard = [UIPasteboard generalPasteboard];
    [pasteBoard setValue:attributedString forPasteboardType:(NSString *)kUTTypeRTF];
    

    but this crash with:

    -[UIPasteboard setValue:forPasteboardType:]: value is not a valid property list type'
    

    which is to be expected, because NSAttributedString is not a property list value.

    If the user paste the content of the pasteboard in my app, I would like to keep all the standards and custom attributes of the attributed string.

    4 Solutions Collect From Internet About “Copy NSAttributedString in UIPasteBoard”

    I have found that when I (as a user of the application) copy rich text from a UITextView into the pasteboard, the pasteboard contains two types:

    "public.text",
    "Apple Web Archive pasteboard type
    

    Based on that, I created a convenient category on UIPasteboard.
    (With heavy use of code from this answer).

    It works, but:
    The conversion to html format means I will lose custom attributes. Any clean solution will be gladly accepted.

    File UIPasteboard+AttributedString.h:

    @interface UIPasteboard (AttributedString)
    
    - (void) setAttributedString:(NSAttributedString *)attributedString;
    
    @end
    

    File UIPasteboard+AttributedString.m:

    #import <MobileCoreServices/UTCoreTypes.h>
    
    #import "UIPasteboard+AttributedString.h"
    
    @implementation UIPasteboard (AttributedString)
    
    - (void) setAttributedString:(NSAttributedString *)attributedString {
        NSString *htmlString = [attributedString htmlString]; // This uses DTCoreText category NSAttributedString+HTML - https://github.com/Cocoanetics/DTCoreText
        NSDictionary *resourceDictionary = @{ @"WebResourceData" : [htmlString dataUsingEncoding:NSUTF8StringEncoding],
        @"WebResourceFrameName":  @"",
        @"WebResourceMIMEType" : @"text/html",
        @"WebResourceTextEncodingName" : @"UTF-8",
        @"WebResourceURL" : @"about:blank" };
    
    
    
        NSDictionary *htmlItem = @{ (NSString *)kUTTypeText : [attributedString string],
            @"Apple Web Archive pasteboard type" : @{ @"WebMainResource" : resourceDictionary } };
    
        [self setItems:@[ htmlItem ]];
    }
    
    
    @end
    

    Only implemented setter. If you want to write the getter, and/or put it on GitHub, be my guest 🙂

    @Guillaume’s approach using HTML doesn’t work for me (at least in iOS 7.1 beta 5).

    The cleaner solution is to insert NSAttributedStrings as RTF (plus plaintext fallback) into the paste board:

    - (void)setAttributedString:(NSAttributedString *)attributedString {
        NSData *rtf = [attributedString dataFromRange:NSMakeRange(0, attributedString.length)
                                   documentAttributes:@{NSDocumentTypeDocumentAttribute: NSRTFTextDocumentType}
                                                error:nil];
        self.items = @[@{(id)kUTTypeRTF: [[NSString alloc] initWithData:rtf encoding:NSUTF8StringEncoding],
                         (id)kUTTypeUTF8PlainText: attributedString.string}];
    }
    

    Swift 2.3

    public extension UIPasteboard {
      public func set(attributedString: NSAttributedString?) {
    
        guard let attributedString = attributedString else {
          return
        }
    
        do {
          let rtf = try attributedString.dataFromRange(NSMakeRange(0, attributedString.length), documentAttributes: [NSDocumentTypeDocumentAttribute: NSRTFTextDocumentType])
          items = [[kUTTypeRTF as String: NSString(data: rtf, encoding: NSUTF8StringEncoding)!, kUTTypeUTF8PlainText as String: attributedString.string]]
    
        } catch {
    
        }
      }
    }
    

    Swift 3

    import MobileCoreServices
    public extension UIPasteboard {
      public func set(attributedString: NSAttributedString?) {
    
        guard let attributedString = attributedString else {
          return
        }
    
        do {
          let rtf = try attributedString.data(from: NSMakeRange(0, attributedString.length), documentAttributes: [NSDocumentTypeDocumentAttribute: NSRTFTextDocumentType])
          items = [[kUTTypeRTF as String: NSString(data: rtf, encoding: String.Encoding.utf8.rawValue)!, kUTTypeUTF8PlainText as String: attributedString.string]]
    
        } catch {
    
        }
      }
    }
    

    It is quite simple:

      #import <MobileCoreServices/UTCoreTypes.h>
    
      NSMutableDictionary *item = [[NSMutableDictionary alloc] init];
    
      NSData *rtf = [attributedString dataFromRange:NSMakeRange(0, attributedString.length)
                                 documentAttributes:@{NSDocumentTypeDocumentAttribute: NSRTFDTextDocumentType}
                                              error:nil];
    
      if (rtf) {
        [item setObject:rtf forKey:(id)kUTTypeFlatRTFD];
      }
    
      [item setObject:attributedString.string forKey:(id)kUTTypeUTF8PlainText];
    
      UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
      pasteboard.items = @[item];
    

    The pasteboard manager in OSX can auto convert between a lot of textual and image types.

    For rich text types, you’d usually place RTF into the pasteboard. You can create RTF representation from an attributed string, and vice versa. See the “NSAttributedString Application Kit Additions Reference”.

    If you have images included as well, then use the RTFd instead of RTF flavor.

    I don’t know the MIME types for these (I’m used to the Carbon Pasteboard API, not the Cocoa one), but you can convert between UTIs, Pboard and MIME Types using the UTType API.

    UTI for RTF is “public.rtf”, for RTFd it’s “com.apple.flat-rtfd”.