Custom Data
To be used with a pasteboard, an object must conform to the NSPasteboardWriting
and/or NSPasteboardReading
protocols. You can make your own custom objects conform to these protocols, or you can use instances of NSPasteboardItem
to contain custom data.
Overview
Any object that you put on a pasteboard must conform to the NSPasteboardWriting
protocol; to retrieve an instance of an object from the pasteboard, it must adopt the NSPasteboardReading
protocol. Several of the common Foundation and Application Kit classes implement both of these protocols, including NSString
, NSImage
, NSURL
, NSColor
, NSAttributedString
, and NSPasteboardItem
.
If you want to be able to write your custom object to a pasteboard, or initialize an instance of your custom class from a pasteboard, your class must also adopt the corresponding protocol. In some situations, it may be appropriate to use NSPasteboardItem
.
Custom Class
For the examples that follow, consider a simple model class that represents a bookmark as you might find in a web browser:
@interface Bookmark : NSObject <NSCoding, NSPasteboardWriting, NSPasteboardReading> { |
} |
@property (nonatomic, copy) NSString *title; |
@property (nonatomic, copy) NSString *notes; |
@property (nonatomic, retain) NSDate *date; |
@property (nonatomic, retain) NSURL *url; |
@property (nonatomic, retain) NSColor *color; |
@end |
Notice that the class adopts the NSCoding
protocol so that it can be archived and unarchived.
Writing
To support writing to a pasteboard, your class must conform to the NSPasteboardWriting
protocol. It has three methods—one of which is optional—as described below. The pasteboard sends these messages to your object when you add your object to the pasteboard, so that it can determine what types of data it will hold and to get the first data representation of your object. The last method may be invoked additional times if your object provides multiple representations.
This method returns an array of UTIs for the data types your object can write to the pasteboard.
The order in the array is the order in which the types should be added to the pasteboard—this is important as only the first type is written initially, the others are provided lazily (see Promised Data).
The method provides the pasteboard argument so that you can return different arrays for different pasteboards. You might, for example, put different data types on the dragging pasteboard than you do on the general pasteboard, or you might put on the same data types but in a different order. You might add to the dragging pasteboard a special representation that indicates the indexes of items being dragged so that they can be reordered.
- (NSArray *)writableTypesForPasteboard:(NSPasteboard *)pasteboard
This method is optional. It returns the options for writing data of a type to a pasteboard.
The only option currently supported is “promised”. You can use this if you want to customize the behavior—for example, to ensure that one particular type is only promised.
- (NSPasteboardWritingOptions)writingOptionsForType:(NSString *)type pasteboard:(NSPasteboard *)pasteboard
This method returns the appropriate property list object representation of your object for the specified type.
- (id)pasteboardPropertyListForType:(NSString *)type
In most cases, you just implement the two required methods—writableTypesForPasteboard:
and pasteboardPropertyListForType:
.
In the example of the Bookmark class, you could implement writableTypesForPasteboard:
as follows:
- (NSArray *)writableTypesForPasteboard:(NSPasteboard *)pasteboard { |
static NSArray *writableTypes = nil; |
if (!writableTypes) { |
writableTypes = [[NSArray alloc] initWithObjects:BOOKMARK_UTI, |
(NSString *)kUTTypeURL, NSPasteboardTypeString, nil]; |
} |
return writableTypes; |
} |
This implementation ignores the pasteboard type, however you might choose to return different types for say, a general and for a dragging pasteboard.
You could implement pasteboardPropertyListForType:
as follows:
- (id)pasteboardPropertyListForType:(NSString *)type { |
if ([type isEqualToString:BOOKMARK_UTI]) { |
return [NSKeyedArchiver archivedDataWithRootObject:self]; |
} |
if ([type isEqualToString:(NSString *)kUTTypeURL]) { |
return [url pasteboardPropertyListForType:(NSString *)kUTTypeURL]; |
} |
if ([type isEqualToString:NSPasteboardTypeString]) { |
return [NSString stringWithFormat:@"<a href=\"%@\">%@</a>", |
[url absoluteString], title]; |
} |
return nil; |
} |
In the example, the method returns either an NSData
or an NSString
object. The pasteboard accepts data values directly, and automatically converts the string to the appropriate data representation. If the method returned any other property list type, the pasteboard would automatically convert it to the appropriate data representation.
Reading
To support reading from a pasteboard, your class must conform to the NSPasteboardReading
protocol. It has three methods, two of which are optional.
This class method returns an array of UTIs for the data types your object can read from the pasteboard.
The method provides the pasteboard argument so that you can return for different arrays for different pasteboards. As with reading, you might put different data types on the general pasteboard than you do on the dragging pasteboard, or you might put on the same data types but in a different order.
+ (NSArray *)readableTypesForPasteboard:(NSPasteboard *)pasteboard
This class method is optional. It allows you to specify the options for reading from a pasteboard.
The options are expressed using a
NSPasteboardReadingOptions
bit field. You can specify that the corresponding data should be read as one of:An
NSData
object (NSPasteboardReadingAsData
)This simply returns the pasteboard data as-is.
A string (
NSPasteboardReadingAsString
)The pasteboard data is returned as an instance of
NSString
.A property list (
NSPasteboardReadingAsPropertyList
)The data on the pasteboard is unserialized as a property list.
An archive (
NSPasteboardReadingAsKeyedArchive
)The data on the pasteboard is treated as a keyed archive.
+ (NSPasteboardReadingOptions)readingOptionsForType:(NSString *)type pasteboard:(NSPasteboard *)pasteboard
This method is optional. You implement this method if it is possible to initialize an instance of your class using a property list.
Important: This method is optional because: if your implementation of
readableTypesForPasteboard:
returns just a single type, and that type uses theNSPasteboardReadingAsKeyedArchive
reading option, theninitWithCoder:
is called instead of this method.The property list object is the
NSData
for that type on the pasteboard, but by specifying anNSPasteboardReading
option for a type, the data can be automatically retrieved as a string or property list.
- (id)initWithPasteboardPropertyList:(id)propertyList ofType:(NSString *)type
In the example of the Bookmark class, you could implement the protocol as follows
First, readableTypesForPasteboard:
specifies that you can initialize a Bookmark using your own custom type and an URL:
+ (NSArray *)readableTypesForPasteboard:(NSPasteboard *)pasteboard { |
static NSArray *readableTypes = nil; |
if (!readableTypes) { |
readableTypes = [[NSArray alloc] initWithObjects:BOOKMARK_UTI, (NSString *)kUTTypeURL, nil]; |
} |
return readableTypes; |
} |
Next, in this case provide an implementation of readingOptionsForType:pasteboard:
to specify that if the type is your custom type you only want to treat the data as a keyed archive, and if the type is an URL type then support the same options as NSURL
.
+ (NSPasteboardReadingOptions)readingOptionsForType:(NSString *)type pasteboard:(NSPasteboard *)pboard { |
if ([type isEqualToString:BOOKMARK_UTI]) { |
/* |
This means you don't need to implement code for this |
type from initWithPasteboardPropertyList:ofType: |
*/ |
return NSPasteboardReadingAsKeyedArchive; |
} |
else if ([type isEqualToString: (NSString *)kUTTypeURL]) { |
return [NSURL readingOptionsForType:type pasteboard:pboard]; |
} |
return 0; |
} |
Finally, implement initWithPasteboardPropertyList:ofType:
as follows:
- (id)initWithPasteboardPropertyList:(id)propertyList ofType:(NSString *)type { |
if (self = [self init]) { |
if ([type isEqualToString:(NSString *)kUTTypeURL]) { |
[url release]; |
url = [[NSURL alloc] initWithPasteboardPropertyList:propertyList ofType:type]; |
[title release]; |
title = [[url absoluteString] retain]; |
} else { |
[self release]; |
return nil; |
} |
} |
return self; |
} |
Notice two things:
The method calls the designated initializer, in this case
init
.The method does not test for the custom Bookmark type. Recall that
readableTypesForPasteboard:
returned just a single type, and thatreadingOptionsForType:pasteboard:
specifiedNSPasteboardReadingAsKeyedArchive
as the sole reading option; thus for the custom Bookmark typeinitWithCoder:
is called instead ofinitWithPasteboardPropertyList:ofType:
.
NSPasteboardItem
Sometimes you want to write items to the pasteboard but you don’t have a convenient wrapper object, or you may want to provide data in a common format but only on demand. For example, you may want to be able to provide a string as an attributed string, a simple string, or as tabular text, where the tabular text is formatted differently from the simple string (so you can’t just write the attributed string directly to the pasteboard).
For these situations, you can use NSPasteboardItem
objects. In general, NSPasteboardItem
objects provide you with fine-grained control over what you might put on the pasteboard. They’re designed to be a temporary objects—they’re only associated with a single pasteboard, and only with one change count, so you shouldn’t maintain them after you’ve created them and put them on the pasteboard.
Writing
The following example illustrates how you might use an NSPasteboardItem
object to create a pasteboard item with three representations—string, attributed string, and tabular text. The data for these representations is promised by a data provider—in this case, self
. The data provider must conform to the NSPasteboardItemDataProvider Protocol
protocol. In its implementation of pasteboard:item:provideDataForType:
, it generates and returns the data requested.
NSPasteboardItem *pasteboardItem = [[NSPasteboardItem alloc] init]; |
NSArray *types = [[NSArray alloc] initWithObjects: |
[NSPasteboardTypeRTF, NSPasteboardTypeString, NSPasteboardTypeTabularText, nil]; |
BOOL ok = [pasteboardItem setDataProvider:self forTypes:types]; |
if (ok) { |
NSPasteboard *pasteboard = [NSPasteboard generalPasteboard]; |
[pasteboard clearContents]; |
ok = [pasteboard writeObjects:[NSArray arrayWithObject:pasteboardItem]]; |
} |
if (ok) { |
// Maintain the information required to provide the data if requested. |
} |
Reading
Suppose there are five items on the pasteboard, two contain TIFF data, two contain RTF data, one contains a private data type. Calling readObjectsForClasses:options:
with just the NSImage
class will return an array containing two image objects. Calling with just the NSAttributedString
class will return an array containing two attributed strings. Calling with both classes (NSImage
and NSAttributedString
) will return two image objects and two attributed strings.
Notice that in the previous examples, the count of objects returned is less than the number of items on the pasteboard. Only objects of the requested classes are returned. If you add the NSPasteboardItem
class to the array, then you will always get back an array that contains the same number of objects as there are items on the pasteboard. Since you provide the elements in the classes array in your preferred order, you should add the NSPasteboardItem
class to the end of the array.
// NSPasteboard *pasteboard = <#Get a pasteboard#>; |
NSArray *classes = [[NSArray alloc] initWithObjects: |
[NSImage class], [NSAttributedString class], [NSPasteboardItem class], nil]; |
NSDictionary *options = [NSDictionary dictionary]; |
NSArray *copiedItems = [pasteboard readObjectsForClasses:classes options:options]; |
if (copiedItems != nil) { |
// Do something with the contents. |
In this case, the method will return an array with two images, two attributed strings, and one pasteboard item containing the private data type.
Copyright © 2010 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2010-09-01