I recently started working with UIActivity to share my app to the world, but I have few problems. First, I didn't find how to set the subject of my email. Is there any way? Second, when I set the body text of my email, there is a extra "enter" (first line of the email is blank and my text starts at the second line). Here's the code:
NSMutableArray *array = [[NSMutableArray alloc] initWithObjects: #"Test", nil];
UIActivityViewController *activityViewController = [[UIActivityViewController alloc]
initWithActivityItems:array applicationActivities:nil];
And in the email, it shows that:
"
Test
"
Third: is there a way to know which sharing method has been selected? Because I want to include a hashtag in my post when the user shares on twitter, but now it gets integrated in the email also, which obviously doesn't make sense.
Thanks!
For adding subject to the email using UIActivityViewController on iOS6, this is the best solution that anyone can use.. All you have to do is call the following while initializing UIActivityViewController.
UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems:activityItems applicationActivities:applicationActivities];
[activityViewController setValue:#"My Subject Text" forKey:#"subject"];
And your UIActivityViewController is populated with a subject.
In iOS7, this is possible by using -
activityViewController:subjectForActivityType:
When posting an item the service may provide for a separate subject
field and data field, such as an email message. Implement this method
if you wish to provide a subject field for services that support one.
Check - https://developer.apple.com/library/ios/documentation/uikit/reference/UIActivityItemSource_protocol/Reference/Reference.html#//apple_ref/occ/intfm/UIActivityItemSource/activityViewController:subjectForActivityType:
1 and 2: How do I set recipients for UIActivityViewController in iOS 6?
Although both provided methods are a bit of a hack, especially the first one, it is possible.
3: it is possible to share different content on different services, but the number of items and their types should be the same (but it is not a limitation, really, as you can return nil for items you don't need on particular service). You have to create sharing items after the service was selected using UIActivityItemSource protocol
Code I use:
Show UIActivityViewController with the current controller as provider of all items (it should have in .h file):
const int numberOfSharedItems = 5;
- (IBAction)shareAction:(id)sender
{
NSMutableArray *shareItems = [NSMutableArray new];
while ([shareItems count] < numberOfSharedItems)
[shareItems addObject: self];
UIActivityViewController *shareController =
[[UIActivityViewController alloc]
// actual items are prepared by UIActivityItemSource protocol methods below
initWithActivityItems: shareItems
applicationActivities :nil];
shareController.excludedActivityTypes = #[UIActivityTypeMessage, UIActivityTypePrint, UIActivityTypeCopyToPasteboard, UIActivityTypeAssignToContact, UIActivityTypeSaveToCameraRoll];
[self presentViewController: shareController animated: YES completion: nil];
}
Make placeholders for items that will be shared:
-(id)activityViewControllerPlaceholderItem:(UIActivityViewController *)activityViewController
{
static UIActivityViewController *shareController;
static int itemNo;
if (shareController == activityViewController && itemNo < numberOfSharedItems - 1)
itemNo++;
else {
itemNo = 0;
shareController = activityViewController;
}
switch (itemNo) {
case 0: return #""; // intro in email
case 1: return #""; // email text
case 2: return [NSURL new]; // link
case 3: return [UIImage new]; // picture
case 4: return #""; // extra text (via in twitter, signature in email)
default: return nil;
}
}
Make actual items that will be shared, differently for different services:
-(id)activityViewController:(UIActivityViewController *)activityViewController itemForActivityType:(NSString *)activityType
{
// the number of item to share
static UIActivityViewController *shareController;
static int itemNo;
if (shareController == activityViewController && itemNo < numberOfSharedItems - 1)
itemNo++;
else {
itemNo = 0;
shareController = activityViewController;
}
NSString *shareText = [self shareText]; // whatever you fancy
NSURL *shareURL = [self shareURL];
// twitter
if ([activityType isEqualToString: UIActivityTypePostToTwitter])
switch (itemNo) {
case 0: return nil;
case 1: return shareText; // you can change text for twitter, I add $ to stock symbol inside shareText here, e.g. Hashtags can be added too
case 2: return shareURL;
case 3: return nil; // no picture
case 4: return #"via #YourApp";
default: return nil;
}
// email
else if ([activityType isEqualToString: UIActivityTypeMail])
switch (itemNo) {
case 0: return #"Hi!\r\n\r\nI used YourApp\r\n";
case 1: return shareText;
case 2: return shareURL;
case 3: return nil; // no picture
case 4: return [#"\r\nCheck it out.\r\n\r\nCheers\r\n" stringByAppendingString: [self userName]];
default: return nil;
}
else // Facebook or something else in the future
switch (itemNo) {
case 0: return nil;
case 1: return shareText;
case 2: return shareURL;
case 3: return [self shareImage];
case 4: return nil;
default: return nil;
}
}
u can simply create a class as follows :
#interface MYNActivityProvider : UIActivityItemProvider <UIActivityItemSource>
#end
// implementation
- (id) activityViewController:(UIActivityViewController *)activityViewController
itemForActivityType:(NSString *)activityType
{
if ( [activityType isEqualToString:UIActivityTypePostToTwitter] ) {
return stringToPost;
}
if ( [activityType isEqualToString:UIActivityTypePostToFacebook] ) {
return stringToPost;
}
if ( [activityType isEqualToString:UIActivityTypeMessage] ) {
return #"SMS message text";
}
if ( [activityType isEqualToString:UIActivityTypeMail] ) {
return #"Email text here!";
}
if ( [activityType isEqualToString:#"Custom"] ) {
return #"app custom text";
}
return nil;
}
You might want to try OvershareKit.
We are frequently asked why someone would use OvershareKit instead of UIActivityViewController (UIAVC) and UIActivity. UIAVC is great for apps that know they’ll never have a need for any of the following:
Never need to integrate with more than one or two third party services.
Never need to tweak the UI for the activity sheet and sharing screens.
Never care to provide separate, media-specific content for each sharing type (email versus SMS, etc.)
Never need to have multiple items such as a Copy Text versus a Copy Link in the same sheet.
Don't mind that all non-system-provided activities get stuck with boring monochromatic icons.
Your situation is (3) - a need to care about different content for different sharing type.
Related
I have added extension to app(as per apple documentation) but intent handler class is not responding with my commands. what i am missing don't no.Please help me ,How can i add siri to my app.
// reslove
- (void)resolveContactsForStartAudioCall:(INStartAudioCallIntent *)intent
withCompletion:(void (^)(NSArray<INPersonResolutionResult *> *resolutionResults))completion{
NSArray *contacts;
NSArray * contacts1;
INPersonResolutionResult* result = nil;
if (contacts == intent.contacts) {
for (INPerson *contact in contacts) {
contacts1 = [self contactsMatchingName:contact.displayName];
switch (contacts1.count) {
case 1:
result = [contacts objectAtIndex:0];
[INPersonResolutionResult successWithResolvedPerson:[contacts1 objectAtIndex:0]];
break;
case 0:
result = [INPersonResolutionResult unsupported];
break;
default:
result = [ INPersonResolutionResult disambiguationWithPeopleToDisambiguate:contacts1];
break;
}
}
completion(contacts1);
}else{
result = [INPersonResolutionResult needsValue];
}
}
// confirmation method
- (void)confirmStartAudioCall:(INStartAudioCallIntent *)intent
completion:(void (^)(INStartAudioCallIntentResponse *response))completion{
INStartAudioCallIntentResponse *result = nil;
if (![[NSUserDefaults standardUserDefaults]objectForKey:#"isSignedIn"]) {
result = [[INStartAudioCallIntentResponse alloc]initWithCode:INStartAudioCallIntentResponseCodeReady userActivity:nil];
completion(result);
}else{
NSUserActivity *activity = [[NSUserActivity alloc]initWithActivityType:#"reLogin"];
result = [[INStartAudioCallIntentResponse alloc]initWithCode:INStartAudioCallIntentResponseCodeFailure userActivity:activity];
completion(result);
}
}
Here are a few resources to get you started.
Guides and Documentation
SiriKit Programming Guide
Intents Framework
IntentsUI Framework
Sample Code
UnicornChat Sample App
Videos (from WWDC 2016)
Introducing SiriKit
Extending your app with SiriKit
This question already has answers here:
WatchKit Option to ONLY Dictate?
(2 answers)
Closed 7 years ago.
According to Apple's doc of WKInterfaceController you can let user to dictate text preseting a new interface controller in this very simple way:
self.presentTextInputControllerWithSuggestions(["suggestion 1", "suggestion 2"] allowedInputMode: .Plain, completion: { (answers) -> Void in
if reply && reply.count > 0 {
if let answer = answers[0] as? String {
println("\answer")
}
}
})
as explained here.
I have seen that Amazon App for Apple Watch let you search for products by tapping the search icon directly
So you get into the Dictation in one step
Through the WKInterfaceController method, we will get something different
Which Apple's API Amazon app is using to enable dictation in this way?
(UPDATE)
I have just find out that it's super easy as explained here
So the final solution I came out was this
- (void) table:(WKInterfaceTable *)table didSelectRowAtIndex:(NSInteger)rowIndex {
if (rowIndex==0) { // handle manual selection
__weak MainInterfaceController *weakSelf = self;
[self presentTextInputControllerWithSuggestions:nil allowedInputMode:WKTextInputModePlain completion:^(NSArray *results) {
if(results && [results count]>0) {
NSString *inputText=nil;
for(NSString *input in results) {
NSLog(#"Input %#", input);
inputText=input;
break;
}
if(inputText!=nil && [inputText length]>0) {
[weakSelf pushControllerWithName:#"Search" context:
[NSDictionary dictionaryWithObjectsAndKeys:
inputText, #"query", nil]
];
}
} else {
NSLog(#"No input provided");
}
}];
}}
Set the mode to .Plain and don't provide any suggestions.
If you remove the suggested strings array, it will go straight into dictation mode.
There is a extremely similar question asked by the following post: Different data for sharing providers in UIActivityViewController. But my question is different.
I know how to share different different data of the same type with different activities by using itemForActivityType. For example:
- (id) activityViewController:(UIActivityViewController *)activityViewController
itemForActivityType:(NSString *)activityType
{
if ( [activityType isEqualToString:UIActivityTypePostToTwitter] )
return #"This is a #twitter post!";
if ( [activityType isEqualToString:UIActivityTypePostToFacebook] )
return #"This is a facebook post!";
if ( [activityType isEqualToString:UIActivityTypeAirDrop] )
return #"Airdrop message text";
else
return nil;
}
However, my question is: what if I have different kind of data to share with different activities, what should I do?. For example, what if I would like to share:
a string on Twitter;
a array of an string and and image on Facebook;
the actual data of the image (e.g. NSData) with Airdrop.
P.S.:
I also looked at the following protocol function:
- (id)activityViewControllerPlaceholderItem:;
However, I cannot use it because we don't know the value of activityType here.
You'd want to create and share two or more objects that conform to the UIActivityItemSource, where one returns the String, another one an Image, etc. Then when the delegate callback requesting the item is called you check which activity type was selected (Facebook, Mail, AirDrop, etc) and have one or multiple of the ItemSource's return nil if that item doesn't apply to that activity. Make sure for any chosen activity that at least one of the item's return a non-nil value.
You can take a look at the airdrop sample code to get some examples of how to implement UIActivityItemSource
For anyone still looking for a solution in objective-c, this is for sharing different datasources, returning more than one object, and it works with whats'app share. In my case I wanted both picture and text for all itemForActivityType:
FIRST: create your UIActivityItemSource, 1 for text, and 1 for the image
MyShareImage.h
#protocol giveMeImageToShare
- (UIImage*)imageToShare;
#end
#interface MyShareImage : NSObject<UIActivityItemSource>
#property (weak,nonatomic) id<giveMeImageToShare> delegate;
#end
MyShareImage.m
#import "MyShareImage.h"
#implementation MyShareImage
- (id)activityViewControllerPlaceholderItem:(UIActivityViewController*)activityViewController{
return #"";
}
- (id)activityViewController:(UIActivityViewController *)activityViewController itemForActivityType:(UIActivityType)activityType{
return [[self delegate] imageToShare];
}
then,
MyShareText.h
#protocol givemetextToShare
- (NSString*)textToShare;
#end
#interface MyShareText : NSObject<UIActivityItemSource>
#property (weak,nonatomic) id<givemetextToShare> delegate;
#end
MyShareText.m
#implementation MyShareText
- (id)activityViewControllerPlaceholderItem:(UIActivityViewController *)activityViewController{
return #"";
}
- (id)activityViewController:(UIActivityViewController *)activityViewController itemForActivityType:(UIActivityType)activityType{
if ([activityType containsString:#"net.whatsapp.WhatsApp.ShareExtension"]) {
return nil;
}
return [[self delegate] textToShare];
}
And now the activityController:
- (void)shareAllPossible:(id)sender withThisImage:(UIImage*)immagineShare andThisText:(NSString*)testoShare{
immagine = immagineShare;
testo = testoShare;
MyShareText *myShareText = [MyShareText new];
myShareText.delegate = self;
MyShareImage *myShareImage = [MyShareImage new];
myShareImage.delegate = self;
NSAssert(immagineShare, #"The image must be loaded to share.");
if (immagineShare) {
UIActivityViewController *activityController = [[UIActivityViewController alloc] initWithActivityItems:#[myShareImage ,myShareText] applicationActivities:nil];
activityController.completionWithItemsHandler = ^(NSString *activityType, BOOL completed, NSArray *returnedItems, NSError *activityError) {
if (completed)
{
//NSLog(#"The Activity: %# was completed", activityType);
}
else {
//NSLog(#"The Activity: %# was NOT completed", activityType);
}
};
[self presentViewController:activityController animated:YES completion:nil];
}
}
Hope it helps.
* got inspiration from https://stackoverflow.com/a/37548529/1907742
Mchurch
I am using the following code to call action sheet sharing in my app:
- (IBAction)sendPost:(id)sender
{
NSArray *activityItems = nil;
UIImage *appIcon = [UIImage imageNamed:#"appIcon.png"];
NSString *postText = [[NSString alloc] initWithFormat:#"LETS ASSUME THIS STRING IS LONGER THAN 140 CHARACTERS THAT TWITTER PROHIBITS BUT CAN STILL BE SHARED VIA FACEBOOK, EMAIL, TEXT"];
activityItems = #[postText,appIcon];
UIActivityViewController *activityController = [[UIActivityViewController alloc] initWithActivityItems:activityItems applicationActivities:nil];
[self presentViewController:activityController animated:YES completion:nil];
}
The problem is this: postText is longer than 140 characters, so sharing via twitter will not be possible, the character count will be -x (red number of characters you are over in order to share via twitter), my question is this: How can I make an exception so that a different message say shortPostText will be the one used when twitter is selected for sharing?
And once the sendPost action is sent I don't see a way to explicitly set a string for twitter, once you are here:
Edit: I dont understand why someone would down-vote this question, I am not asking how to make an if/else statement or how to program. This is a genuine question, that needs a genuine answer.
UPDATE: I need a work around this because this is what I get when a user tries to share via twitter in my app:
A red/negative character indicator and a non-active post button, so unless that character count goes down to 0 or less it will not allow the post to go to twitter.
TL;DR Use UIActivityItemSource to special case payload depending on what the user selection was.
Try this instead:
- (IBAction)sendPost:(id)sender
{
UIImage *appIcon = [UIImage imageNamed:#"appIcon.png"];
NSString *postText = [[NSString alloc] initWithFormat:#"LETS ASSUME THIS STRING IS LONGER THAN 140 CHARACTERS THAT TWITTER PROHIBITS BUT CAN STILL BE SHARED VIA FACEBOOK, EMAIL, TEXT"];
TextItemSource *itemSource = [[TextItemSource alloc] initWithString:postText previewImage:appIcon];
UIActivityViewController *activityController = [[UIActivityViewController alloc] initWithActivityItems:#[itemSource] applicationActivities:nil];
[self presentViewController:activityController animated:YES completion:nil];
}
// ------- TextItemSource.h
#interface TextItemSource : NSObject <UIActivityItemSource>
- (id)initWithString:(NSString *)string previewImage:(UIImage *)previewImage;
#end
// ------- TextItemSource.m
#implementation TextItemSource
{
NSString *_string;
UIImage *_previewImage;
}
- (id)initWithString:(NSString *)string previewImage:(UIImage *)previewImage
{
self = [super init];
if (self) {
_string = [string copy];
_previewImage = previewImage;
}
return self;
}
- (id)activityViewControllerPlaceholderItem:(UIActivityViewController *)activityViewController
{
return _string;
}
- (id)activityViewController:(UIActivityViewController *)activityViewController itemForActivityType:(NSString *)activityType
{
NSString *string = _string;
if ([activityType isEqual:UIActivityTypePostToTwitter]) {
#pragma mark TODO: do smarter thing :)
string = [_subject substringToIndex:140];
}
return string;
}
- (UIImage *)activityViewController:(UIActivityViewController *)activityViewController thumbnailImageForActivityType:(NSString *)activityType suggestedSize:(CGSize)size
{
// might want to scale image to fit suggestedSize
return _previewImage;
}
#end
In the Photos app on the iPhone, when you select the Mail sharing option, the photo animates into the modal view controller that slides up. How is it possible to modify the behaviour of the built-in UIActivities? For example, I'd like to be able to set the subject field of the mail composer.
Unfortunately, customizing the subject field of the UIActivityViewController mail composer is not working yet.
There is a documented and reported bug regarding trying to customize this discussed here:
iphone - How do I set recipients for UIActivityViewController in iOS 6?
If this were working, according to the documentation, you would be able to customize these mail composer fields:
UIActivityTypeMail:
The object posts the provided content to a new email message. When
using this service, you can provide NSString and UIImage objects and
NSURL objects pointing to local files as data for the activity items.
You may also specify NSURL objects whose contents use the mailto
scheme.
So using the mailto scheme, when it is working, you should be able to customize those fields like this:
NSString *text = #"My mail text";
NSURL *recipients = [NSURL URLWithString:#"mailto:foo#bar.com?subject=Here-is-a-Subject"];
NSArray *activityItems = #[text, recipients];
UIActivityViewController *activityController = [[UIActivityViewController alloc] initWithActivityItems:activityItems applicationActivities:nil];
[self presentViewController:activityController animated:YES completion:nil];
If your looking for other ways to customize the UIActivityViewControllerthere is an excellent example project here:
https://github.com/russj/ios6ShareDemo
This is how I did it and it's working for me in iOS 7.
Create a class that conforms to the UIActivityItemSource protocol:
#interface CustomActivityItem : NSObject <UIActivityItemSource>
#end
In the implementation override the relevant methods:
- (id)activityViewControllerPlaceholderItem:(UIActivityViewController *)activityViewController
{
return #"";
}
- (NSString *)activityViewController:(UIActivityViewController *)activityViewController subjectForActivityType:(NSString *)activityType
{
if ([activityType isEqualToString:UIActivityTypeMail])
{
return #"Subject"
}
return nil;
}
- (id)activityViewController:(UIActivityViewController *)activityViewController itemForActivityType:(NSString *)activityType
{
if ([activityType isEqualToString:UIActivityTypeMail])
{
return #"body";
}
return nil;
}
Then present the activity view controller:
CustomActivityItem* activityItem = [[CustomActivityItem alloc] init];
UIActivityViewController* activityViewController = [[UIActivityViewController alloc] initWithActivityItems:#[activityItem]
applicationActivities:nil];
[self presentViewController:activityViewController animated:YES completion:nil];