I am using UIWebView to open a Docx file which is in the form of NSDATA passed from another controller to webview. I am using UIAcvitityController to share content on social media as well. It works fine for Image types and PDF types but it does not work for DOCX and XLSX. Anything special I need to do here?
UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems:self.downloadData applicationActivities:self.applicationActivities];
You need use custom data for activityItems, and implement methods in protocol UIActivityItemSource. follow it:
file: ShareData.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#interface ShareData : NSObject<UIActivityItemSource>
#property (nonatomic, strong) NSData *data;
#property (nonatomic, strong) NSURL *url;
#end
file:ShareData.m
#import "ShareData.h"
#implementation ShareData
-(id)activityViewControllerPlaceholderItem:(UIActivityViewController *)activityViewController {
return self.data;
}
-(id)activityViewController:(UIActivityViewController *)activityViewController itemForActivityType:(NSString *)activityType {
if ([activityType isEqualToString:UIActivityTypeAirDrop]) {
return self.data;
}
return self.url;
}
-(NSString*)activityViewController:(UIActivityViewController *)activityViewController subjectForActivityType:(NSString *)activityType {
return #"share data";
}
-(NSString*)activityViewController:(UIActivityViewController *)activityViewController dataTypeIdentifierForActivityType:(NSString *)activityType {
if ([activityType isEqualToString:UIActivityTypeAirDrop]) {
return #"test";
}
return nil;
}
#end
And, share content on social media:
ShareData *share = [ShareData new];
share.data = downloadData; // one of your downloadData
share.url = downloadURL; // url for the downloadData
UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems:#[share] applicationActivities:nil];
The above code has been tested , it does work for DOCX and XLSX.
The source demo is here: https://github.com/ocarol/UIActivityViewControllerDemo
Supported Data Types by UIActivityViewController
Related
I am using UIActivityViewController and subclassing UIActivityItemSource for sharing of text and image via the apps installed on my iPhone.
After some investigation, If found that it is not possible to share "text" and "Image" with the Instagram app.
So we decided to overlay the text (Instagram caption) over the image itself (static image, in my case is Lion.png, included in the resource folder). But I find that if I were to share the "text overlayed image" using the Instagram app (displayed using UIActivityViewController), though the Instagram app launches with the image, when I enter a caption and hit the share button, though it may seem that the share was successful, but the image does not get shared.
The sharing of the modified png via the email client is successful. Not sure why Instagram is failing.
If I decide to share the original image without the "text overlay" via Instagram, the share is successful on Instagram.
Note: This below code, I have extracted from my project and put in a sample project.
#import "ViewController.h"
#import "EmailItemProvider.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(UIImage*) drawText:(NSString*) text
inImage:(UIImage*) image
atPoint:(CGPoint) point
{
UIFont *font = [UIFont boldSystemFontOfSize:14];
UIGraphicsBeginImageContext(image.size);
[image drawInRect:CGRectMake(0,0,image.size.width,image.size.height)];
CGRect rect = CGRectMake(point.x, point.y, image.size.width, image.size.height);
// [[UIColor whiteColor] set];
// [text drawInRect:CGRectIntegral(rect) withFont:font];
/// Make a copy of the default paragraph style
NSMutableParagraphStyle* paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
paragraphStyle.lineBreakMode = NSLineBreakByCharWrapping;
paragraphStyle.alignment = NSTextAlignmentLeft;
NSDictionary *attributes = #{ NSFontAttributeName: font, NSForegroundColorAttributeName: [UIColor whiteColor],NSParagraphStyleAttributeName: paragraphStyle };
// draw text
[text drawInRect:rect withAttributes:attributes];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
- (NSString*)saveImageFile:(UIImage *)uiimage
{
NSData *data = UIImagePNGRepresentation(uiimage);
NSString *filePath = [NSString stringWithFormat:#"%#/sample.png" ,[NSHomeDirectory() stringByAppendingPathComponent:#"Documents"]];
[[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];
[data writeToFile:filePath atomically:YES];
return filePath;
}
#define SEND_TO_MESSAGE #"Share via Message"
#define SEND_TO_MAIL #"Share via Mail"
- (IBAction)ShareOptions:(id)sender {
UIImage *annotatedFile = [self drawText: #"Referral msg with code" inImage:[UIImage imageNamed:#"Lion"] atPoint: CGPointMake(0, 0)];
NSString *imageFilePath = [self saveImageFile:annotatedFile];
NSMutableDictionary *shareOptionDic=[[NSMutableDictionary alloc] init];
[shareOptionDic setObject:SEND_TO_MESSAGE forKey:#"1"];
[shareOptionDic setObject:SEND_TO_MAIL forKey:#"2"];
UIPasteboard *pb = [UIPasteboard generalPasteboard];
[pb setString:#"Referral message copied to the clipboard."];
EmailItemProvider *emailItem = [EmailItemProvider new];
emailItem.subject = #"sample subject";//Dummy. overridden in the delegate methods of EmailItemProvider.
emailItem.body = #"sample body";//Dummy. overridden in the delegate methods of EmailItemProvider.
//Image with the text overlay. When this image is used, the Instagram share fails.
emailItem.imagePath = imageFilePath;
UIActivityViewController *activityViewController =
[[UIActivityViewController alloc] initWithActivityItems:#[emailItem]
applicationActivities:nil];
activityViewController.excludedActivityTypes = #[UIActivityTypeAssignToContact, UIActivityTypePrint,UIActivityTypeAirDrop];
[self presentViewController:activityViewController animated:TRUE completion:nil];
return;
}
#end
The class EmailItemProvider is subclassed from UIActivityItemSource and it's .h and .m is provided below.
//
// EmailItemProvider.h
//
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#interface EmailItemProvider : NSObject <UIActivityItemSource>
#property (nonatomic, strong) NSString *subject;
#property (nonatomic, strong) NSString *body;
#property (nonatomic, strong) UIImage *image;//dummy
#property (nonatomic, strong) NSString *imagePath;//image path with text overlay
#end
//
// EmailItemProvider.m
//
//
#import "EmailItemProvider.h"
#implementation EmailItemProvider
- (id)activityViewControllerPlaceholderItem:(UIActivityViewController *)activityViewController {
//This code works.
//return [UIImage imageNamed:#"Lion"];
//Returning an text overlayed image for Instagram share doesnot work.
return [UIImage imageWithContentsOfFile:self.imagePath];
}
- (id)activityViewController:(UIActivityViewController *)activityViewController itemForActivityType:(NSString *)activityType {
NSLog(#"one %#", activityType);
//This code which return an image overlayed with text, instagram share fails.
return #{#"text": #"Referral information goes here.", #"image": [UIImage imageWithContentsOfFile:self.imagePath]};
//I am able to share Instagram share when I comment the above code and uncomment the below code.
//return #{#"text": #"Referral information goes here.", #"image": [UIImage imageNamed:#"Lion"]};
}
- (nullable UIImage *)activityViewController:(UIActivityViewController *)activityViewController thumbnailImageForActivityType:(nullable UIActivityType)activityType suggestedSize:(CGSize)size; // if activity supports preview image. iOS 7.0
{
NSLog(#"two activity type : %#\n", activityType);
return [UIImage imageNamed:#"Lion"];
}
- (NSString *)activityViewController:(UIActivityViewController *)activityViewController subjectForActivityType:(NSString *)activityType {
NSLog(#"three %#", activityType);
return #"subject text";
}
#end
I think that the issue was because of the image size. The Instagram share was failing when the source image (super imposed with text) was 236 × 374.
But when I used an 442 × 620 png image as the base image, I was able to share the image after superimposing the text.
Though one question remains unanswered. How is that the vanilla 236 × 374 image (without the text superimposition) being shared successfully via Instagram?
when I use UIActivityViewController to share video, the ActivityView display facebook ins Twitter Tumblr etc but there is not youtube so I want to write a custom UIActivity for sharing video in youtube But I my English is too poor to understand YouTube API Documentation 。。。
here is my code
import
#interface RFCustomYoutubeActivity : UIActivity
#end
#import "RFCustomYoutubeActivity.h"
#interface RFCustomYoutubeActivity ()
#property (nonatomic,strong)NSArray *activityItems;
#end
#implementation RFCustomYoutubeActivity
+ (UIActivityCategory)activityCategory {
return UIActivityCategoryShare;
}
- (NSString *)activityType{
NSString *bundeID = [NSBundle mainBundle].bundleIdentifier;
NSString *typeStr =[bundeID stringByAppendingString:NSStringFromClass([RFCustomYoutubeActivity class])];
return typeStr;
}
- (NSString *)activityTitle {
return #"Youtube";
}
- (UIImage *)activityImage {
return [UIImage imageNamed:#"youtube.png"];
}
- (BOOL)canPerformWithActivityItems:(NSArray *)activityItems{
for (NSObject *obj in activityItems) {
if ([obj isKindOfClass:[NSURL class]] ) {
return YES;
}
}
return NO;
}
- (void)prepareWithActivityItems:(NSArray *)activityItems{
NSMutableArray *mutableArray=[NSMutableArray array];
for (NSObject *obj in activityItems) {
if ([obj isKindOfClass:[NSURL class]]) {
[mutableArray addObject:obj];
}
}
self.activityItems=[NSArray arrayWithArray:mutableArray];
}
- (void)performActivity{
//self.activityItems[0] is a video urlPath.
//here i don't konw how to open youtube(app) or share video to youtube
//or how to open youtube album
}
- (void)activityDidFinish:(BOOL)completed{
NSLog(#"end");
}
I have code to show a document as follows:
documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:self.thisUrl];
NSString *pathExtension = [self.thisUrl pathExtension];
if (pathExtension) {
NSString *UTI = (__bridge NSString*)UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)(pathExtension), NULL);
if (UTI) {
documentInteractionController.UTI = UTI;
}
}
documentInteractionController.delegate = self;
[documentInteractionController presentOptionsMenuFromBarButtonItem:shareButton animated:YES];
When the options menu is displayed, it shows a list of apps that can open the document (e.g. Message), along with a list of actions below.
The options menu shows a list actions that is different from the menu shown in e.g., the Mail app.
The main difference is that the Mail app shows a "print" option, while my options menu does not. How do I get the options menu to show the print option?
EDIT:
I did a further test where I implemented the methods:
- (BOOL)documentInteractionController:(UIDocumentInteractionController *)controller canPerformAction:(SEL)action
{
return YES;
}
- (BOOL)documentInteractionController:(UIDocumentInteractionController *)controller performAction:(SEL)action
{
return YES; // or NO, doesn't matter
}
This had the effect of showing the "print", "copy" and "save to camera roll" actions in the popup view. Nothing happened when I tapped them, probably because I didn't properly implement -performAction. I also get a warning in the console log about using legacy methods.
This was a step backwards in some ways because I could no longer print some documents which were able to print correctly with the document interaction controller before I added those methods.
Apple encourage you to use UIActivityViewController. You can easily achieve this with that. However Print option is available only if your sharing content type supports printing. You can see a list of supported activities by data types here
- (IBAction)shareButton:(UIBarButtonItem *)sender
{
NSString *textToShare = #"Text to share";
NSURL *myWebContent = [NSURL URLWithString:#"http://yourpath.com/yourfile.pdf"]; // set your printable file here!
NSData *myData = [NSData dataWithContentsOfURL:myWebContent];
NSArray *objectsToShare = #[textToShare, myData];
UIActivityViewController *activityVC = [[UIActivityViewController alloc] initWithActivityItems:objectsToShare applicationActivities:nil];
//Add exclusions here
NSArray *excludeActivities = #[UIActivityTypeAirDrop,
UIActivityTypeAssignToContact,
UIActivityTypeSaveToCameraRoll,
UIActivityTypeAddToReadingList,
UIActivityTypePostToFlickr,
UIActivityTypePostToVimeo];
activityVC.excludedActivityTypes = excludeActivities;
[self presentViewController:activityVC animated:YES completion:nil];
}
I got this working using the QuickLook framework. I don't know why the "print" option sometimes doesn't appear for the document interaction controller, but then again, apparantly noone else does either.
The QuickLook framework supports previewing some document types but not all, so I left in my previous view controller and the document interaction controller for those unsupported types.
Below is a snippet of my working code.
#interface PreviewItemDataSource ()
#property (nonatomic, retain) NSURL* item;
#end
#implementation PreviewItemDataSource
#synthesize item=_item;
+(PreviewItemDataSource*)dataSourceWithItem:(NSURL*)item
{
PreviewItemDataSource *source = [[PreviewItemDataSource alloc] init];
source.item = item;
return source;
}
-(NSInteger) numberOfPreviewItemsInPreviewController:(QLPreviewController*)controller {
return 1;
}
- (id<QLPreviewItem>) previewController:(QLPreviewController*)controller previewItemAtIndex:(NSInteger)index {
return self.item;
}
#end
#interface AppDelegate ()
#property (nonatomic, retain) PreviewItemDataSource *dataSource;
#end
...
-(void) openExternalFile:(NSString*) filePath withDelegate:(id<ChildBrowserDelegate>)delegate
{
if ([filePath length] == 0)
return;
NSURL *item = [NSURL URLWithString:filePath];
if (item && [QLPreviewController canPreviewItem:item]) {
[self openQuickLookForItem:item];
} else {
// previous method unchanged
}
}
- (void) openQuickLookForItem:(NSURL*)item {
QLPreviewController *controller = [[QLPreviewController alloc] init];
PreviewItemDataSource *dataSource = [PreviewItemDataSource dataSourceWithItem:item];
controller.dataSource = dataSource;
controller.modalPresentationStyle = UIModalPresentationFullScreen;
[controller setCurrentPreviewItemIndex:0];
[self.viewController presentViewController:controller animated:YES completion:nil];
self.dataSource = dataSource;
}
I am trying to share images via UIActivityViewController similar to this. However if I share several images, Twitter and Facebook will disappear in the UIActivityViewController.
Is there a way to share one image for both Twitter and Facebook, several images for mail as attachment?
EDIT:
// return different string depends on the type
CustomActivityItemProvider *textProvider = [[CustomActivityItemProvider alloc] initWithText:textContent url:url title:textTitle];
NSMutableArray *applicationActivities = [NSMutableArray array];
NSMutableArray *activityItems = [#[
textProvider,
image,
url
] mutableCopy];
// custom applicationActivities
...
// If add multiple images, facebook and twitter will not show up
for(int i = 0; i < [images count]; ++i)
{
if(images[i] != image) [activityItems addObject:images[i]];
}
UIActivityViewController *activityController = [[UIActivityViewController alloc] initWithActivityItems:activityItems
applicationActivities:applicationActivities];
[activityController setValue:textTitle forKey:#"subject"];
activityController.excludedActivityTypes = excludeActivities;
[self presentViewController:activityController animated:YES completion:nil];
Is there a way similar to the UIActivityItemProvider?
A similar way to textActivityItemProvider I've end up using:
ImageActivityItemProvider.h
#import <UIKit/UIKit.h>
#interface ImageActivityItemProvider : UIActivityItemProvider
#property (nonatomic, strong, readonly) UIImage *image;
#property (nonatomic, readonly) NSInteger index;
#property (nonatomic, readonly) NSInteger shouldShowIndex;
- (instancetype)initWithImage:(UIImage*)image index:(NSInteger)index shouldShowIndex:(NSInteger)shouldShowIndex;
#end
ImageActivityItemProvider.m
#import "ImageActivityItemProvider.h"
#interface ImageActivityItemProvider ()
#property (nonatomic, strong) UIImage *image;
#property (nonatomic) NSInteger index;
#property (nonatomic) NSInteger shouldShowIndex;
#end
#implementation ImageActivityItemProvider
- (instancetype)initWithImage:(UIImage*)image index:(NSInteger)index shouldShowIndex:(NSInteger)shouldShowIndex
{
// make sure the placeholder is nil instead of the image
self = [super initWithPlaceholderItem:nil];
if (self)
{
self.image = image;
self.index = index;
self.shouldShowIndex = shouldShowIndex;
}
return self;
}
- (id)item
{
if (
[self.activityType isEqualToString:UIActivityTypeMail] ||
self.index == self.shouldShowIndex
)
{
return self.image;
}
return self.placeholderItem;
}
#end
I am writing a class for getting image from Photos Library.
I want one single method that will return selected image from library.
So i started writing a class named MediaBrowser.
I used block that will give selected image. But I am confused where to write block definition. Please correct the code if i am going wrong.
In MediaBrowser.h
#interface MediaBrowser : NSObject
typedef UIImage* (^MediaBrowserCompletionHandler)(void);
+ (id)sharedInstance;
- (BOOL)startMediaBrowserFromViewController:(UIViewController*)controller
completionHandler:(MediaBrowserCompletionHandler)completion;
#end
In MediaBrowser.m
#interface MediaBrowser () <UIImagePickerControllerDelegate, UINavigationControllerDelegate>
#property (nonatomic, strong) MediaBrowserCompletionHandler completionHandler;
#end
#implementation MediaBrowser
static MediaBrowser *sharedMediaBrowser = nil;
+ (id)sharedInstance
{
if (nil != sharedMediaBrowser) {
return sharedMediaBrowser;
}
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMediaBrowser = [[MediaBrowser alloc] init];
});
return sharedMediaBrowser;
}
- (BOOL)startMediaBrowserFromViewController:(UIViewController *)controller completionHandler:(MediaBrowserCompletionHandler)completion
{
self.completionHandler = [completion copy];
if (([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeSavedPhotosAlbum] == NO)
|| (controller == nil))
return NO;
UIImagePickerController *mediaUI = [[UIImagePickerController alloc] init];
mediaUI.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;
// Displays saved pictures from the Camera Roll album.
mediaUI.mediaTypes = [[NSArray alloc] initWithObjects:(NSString *)kUTTypeImage, nil];
// Hides the controls for moving & scaling pictures, or for
// trimming movies. To instead show the controls, use YES.
mediaUI.allowsEditing = NO;
mediaUI.delegate = self;
[controller presentModalViewController:mediaUI animated:YES];
return YES;
}
// UIImagePickerControllerDelegate
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
NSString *mediaType = [info objectForKey: UIImagePickerControllerMediaType];
UIImage *imageToUse;
// Handle a still image picked from a photo album
if (CFStringCompare ((CFStringRef) mediaType, kUTTypeImage, 0) == kCFCompareEqualTo) {
imageToUse = (UIImage *) [info objectForKey:UIImagePickerControllerOriginalImage];
// Do something with imageToUse
if (self.completionHandler) {
// Pass here UIImage
self.completionHandler();
}
}
[picker dismissModalViewControllerAnimated:YES];
}
#end
I figured out the answer. I wanted to send selected image from UIImagePickerController to the calling class. I was writing wrong block. See corrected code below.
In MediaBrowser.h
block declaration shoudld be :
typedef void (^MediaBrowserCompletionHandler)(UIImage *selectedImage);
And in MediaBroser.m
Calling block should be :
if (self.completionHandler) {
self.completionHandler(imageToUse);
}
typedef UIImage* (^MediaBrowserCompletionHandler)(void);
#interface MediaBrowser : NSObject
#property (nonatomic,copy) MediaBrowserCompletionHandler handler;
#end
This should be fine.
typedef UIImage* (^MediaBrowserCompletionHandler)(void);
#interface MediaBrowser : NSObject
....
#end