Question

I'm pretty new to Objective-C, but have been mostly understanding everything so far. I am stuck, however, on trying to share an animated GIF through NSSharingService.

I am attaching the image like so, where image is a string containing the URL of an animated GIF (https://i.stack.imgur.com/yMmpI.gif for example):

NSImage *imageData = [[NSImage alloc] initWithContentsOfURL:[NSURL URLWithString:image]];
NSArray *shareItems = [NSArray arrayWithObjects:imageData, href, nil];
NSSharingService *service = [NSSharingService sharingServiceNamed:NSSharingServiceNameComposeMessage];
service.delegate = self;
[service performWithItems:shareItems];

When the code is run and the message is sent, however, the image gets sent as a PNG file instead of a GIF.

My suspicion is that the image is either being flattened by NSImage or NSData, and that I need to first save the image to the disk and then attempt to send it. I am wondering, though, if this can be accomplished without that extra step of saving.

Edit 1:

I found a GitHub repo which was attempting to answer a similar problem. A solution was never found, however, but the last bit of advice was:

However, when I add an NSAttributedString with a GIF attachment to NSSharingServicePicker, the shared image is not animated. I can't add the wrapper RTFD data to the picker, as it can only share objects that support NSPasteboardWriting protocol, and the RTFD is returned as NSData.

Copying RTFD data to pasteboard as NSRTFDPboardType works and preserves animation

Would it be possible to convert the GIF to an RTDF object, copy it to the pasteboard, retrieve the pasteboard item, then share that object? Or is it impossible to preserve animation with NSSharingService?

Edit 2:

As @Cocoadelica mentioned in the comments, I'm wondering if CoreImage might be required to preserve animation. I attempted saving the GIF file to the hard drive first, then loading it into NSImage, but it once again converted it into a static PNG.

This is very, very, very frustrating.

Was it helpful?

Solution

I ended up getting a response via the Cocoa-dev mailing list. Basically, you need to attach an NSURL directly linking to the file. It doesn't work for external images, and NSImage is never used:

NSString *fileUrl = @"http://i.imgur.com/V8w9fKt.gif";
NSString *fileName = [fileUrl lastPathComponent];
NSURL *saveUrl = [NSURL URLWithString:[NSString stringWithFormat:@"file://%@", NSTemporaryDirectory()]];
saveUrl = [saveUrl URLByAppendingPathComponent:fileName];
// Write image to temporary directory
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:fileUrl]];
[data writeToURL:saveUrl atomically:YES];

// Attach the raw NSURL pointing to the local file
NSArray *shareItems = [NSArray arrayWithObjects:saveUrl, @"Text", nil];

// Open share prompt
NSSharingService *service = [NSSharingService sharingServiceNamed:NSSharingServiceNameComposeMessage];
service.delegate = self;
[service performWithItems:shareItems];

I then implemented didShareItems and didFailToShareItems so that I could remove the file after sharing was complete:

- (void)sharingService:(NSSharingService *)sharingService didShareItems:(NSArray *)items{
    NSString *path = items[0];
    [self removeFile:path];
}

...

- (void)removeFile:(NSString *)path{
    [[NSFileManager defaultManager] removeItemAtPath:path error:NULL];
}

And for those struggling, I found the following method was required for everything to work properly:

- (NSWindow *)sharingService:(NSSharingService *)sharingService sourceWindowForShareItems:(NSArray *)items sharingContentScope:(NSSharingContentScope *)sharingContentScope{
    return self.window;
}

I realize some of that code is improper (my URLWithString creation is counterintuitive, but I'm learning), but this should get those struggling a starting point.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top