Different data for sharing providers in UIActivityViewController - objective-c

I'm trying to use an UIActivityViewController with one long NSString as the data. If I put a string > 140 characters, the tweet sheet in it does not display the string. And if I truncate the string before giving it to the controller, all of the UIActivities have the truncated string. I don't want Facebook or Message to be truncated.
Is there a way to give different strings to different UIActivities?
Thank you!
(e.g. Marco Arment's The Magazine app does this by having a truncated string followed by #TheMagazineApp in UIActivityPostToTwitter, and other stuff in other UIActivities.)

I think this is what you're looking for: Custom UIActivityViewController icons and text.
You should be able to provide different data for each activity type.

Hope this helps somebody. It's pretty straightforward if you subclass UIActivityItemProvider:
#interface MyActivityItemProvider : UIActivityItemProvider
#end
#implementation MyActivityItemProvider
- (id)item
{
// Return nil, if you don't want this provider to apply
// to a particular activity type (say, if you provide
// print data as a separate item for UIActivityViewController).
if ([self.activityType isEqualToString:UIActivityTypePrint])
return nil;
// The data you passed while initialising your provider
// is in placeholderItem now.
if ([self.activityType isEqualToString:UIActivityTypeMail] ||
[self.activityType isEqualToString:UIActivityTypeCopyToPasteboard])
{
return self.placeholderItem;
}
// Return something else for other activities. Obviously,
// you can as well reuse the data in placeholderItem here.
return #"Something else";
}
#end
Then pass its instance with an array of activity items to UIActivityViewController:
MyActivityItemProvider *activityItem =
[[MyActivityItemProvider alloc] initWithPlaceholderItem:#"Your data"];
NSArray *sharingItems = [NSArray arrayWithObjects:
activityItem, _myUITextView.viewPrintFormatter, nil];
UIActivityViewController *activityController =
[[UIActivityViewController alloc]
initWithActivityItems:sharingItems applicationActivities:nil];

This can be easily done by using the optional activityType property from the UIActivityItemProvider object. That property returns a UIActivityType, so you can do something like:
class PhotoActivityItemProvider: UIActivityItemProvider {
...
override var item: Any {
guard let activityType = self.activityType else {
return photoURL.absoluteString
}
if activityType == .mail || activityType == .message {
return "The photo link is \(photoURL.absoluteString)."
}
...
}
More information in my blog post: https://www.whitesmith.co/blog/control-what-youre-sharing/

You can create a class that conforms to UIActivityItemSource and then pass its instance with an array of activity items to UIActivityViewController:, as #Mu-Sonic suggested.
If you want to know in which platform is the user sharing and return any specific data dependent on the tapped platform, override public func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any?

Related

UIImage only for the chosen applicationActivities, but NSString for all applicationActivities

I'm using UIActivityViewController for share NSString and UIImage, but I don't want to share the UIImage with iMessage & e-mail.
How can I create such a custom UIActivity for UIImage and leave NSString for all?
When you create your UIActivityViewController, you pass along an array of activity items.
And the objects you pass along in that array can be objects that adopt the UIActivityItemSource protocol.
And if you send along a UIImage, why not create a "ImageItemProvider" object and then implement your own "activityViewController:itemForActivityType:" method that returns "nil" if you don't want to share the image via iMessage or email.
E.G.:
//- Returns the data object to be acted upon. (required)
- (id)activityViewController:(UIActivityViewController *)activityViewController itemForActivityType:(NSString *)activityType
{
if ([activityType isEqualToString:UIActivityTypeMail]) {
return nil;
}
if ([activityType isEqualToString:UIActivityTypeMessage]) {
return nil;
}
return [UIImage imageNamed:#"the image you wanted returned"];
}
I'm basing this answer on the code I found in this tutorial.

UIActivityItemSource Protocole set complex object

I'm using iOS 6 new way to share information : UIActivityViewController. To select the shared data depending on the media (facebook, twitter or mail) my view controller implement the UIActivityItemSource Protocol as follow :
- (IBAction)onShareButton:(UIButton *)sender
{
UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems:#[self] applicationActivities:nil];
activityViewController.excludedActivityTypes = #[UIActivityTypeMessage, UIActivityTypeAssignToContact, UIActivityTypeCopyToPasteboard, UIActivityTypeMessage, UIActivityTypePostToWeibo, UIActivityTypePrint, UIActivityTypeSaveToCameraRoll];
[self presentViewController:activityViewController animated:YES completion:^{}];
}
#pragma mark - UIActivityItemSource Protocol
- (id)activityViewController:(UIActivityViewController *)activityViewController itemForActivityType:(NSString *)activityType {
if ([activityType isEqualToString:UIActivityTypePostToFacebook]) {
NSArray *items = #[#"message facebook", [NSURL URLWithString:#"http://www.myUrlFacebook.com"]];
return items;
} else if ([activityType isEqualToString:UIActivityTypePostToTwitter]) {
NSArray *items = #[#"message twitter", [NSURL URLWithString:#"http://www.myUrlTwitter.com"]];
return items;
} else if ([activityType isEqualToString:UIActivityTypeMail]) {
NSArray *items = #[#"message mail", [NSURL URLWithString:#"http://www.myUrlMail.com"]];
return items;
}
NSArray *items = #[#"Not a proper Activity", [NSURL URLWithString:#"http://www.myUrlMail.com"]];
return items;
}
- (id)activityViewControllerPlaceholderItem:(UIActivityViewController *)activityViewController {
return #"PlaceHolder";
}
When I'm returning a simple NSString for activityViewController:itemForActivityType: the string is well used by my UIActivityViewController, but I can't find a way to use an Array !
According to Apple Documentation it should be possible :
This method returns the actual data object to be acted on by an activity object
Apple documentation
Does anyone ever use this UIActivityItemSource Protocol with Arrays, or is there a use full tutorial to do that ?
Note : I also got this error on the console, it may help ...
Launch Services: Registering unknown app identifier com.apple.mobilemail failed
Launch Services: Unable to find app identifier com.apple.mobilemail
A single object conforming to UIactivityItemSource can only return a single piece of data for activityViewControllerPlaceholderItem:, no NSArrays.
You could overcome this by creating and passing two UIActivityItemSources in the activityItems part of the initial initWithActivityItems:. Each source can pass a placeholder value, but can return something blank on itemForActivityType so you don't actually have to use that particular type of data depending on the activity.
Or just use that cool extension mentioned in the other answer.
After spending a significant amount of time trying to figure this one out, it seems it isn't possible to pass it an NSArray of items. So I extended UIActivityViewController to make it possible.
RDActivityViewController

How to get localized Cancel, Done and etc?

UIBarButtonItem have identifiers like Cancel, Done and some others. They are shown as text to user. If user changes language then for example Cancel button will be translated automatically. And as developer you do not need to provide localization string for this buttons. It means that Cancel, Done and other strings already localized and comes together with OS.
Is here a way to get this strings programmatically?
I do not want to add additional strings to localization files. And if it is possible to access then it would be very good.
Here's a little macro I created to get the System UIKit Strings:
#define UIKitLocalizedString(key) [[NSBundle bundleWithIdentifier:#"com.apple.UIKit"] localizedStringForKey:key value:#"" table:nil]
Use it like this:
UIKitLocalizedString(#"Search");
UIKitLocalizedString(#"Done");
UIKitLocalizedString(#"Cancel");
...
One (admittedly questionable) way of accomplishing this easily is use Apple's framework bundle localizations directly:
To see what they have to offer, open the following directory via the Finder:
/Applications/Xcode/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator6.1.sdk/System/Library/Frameworks
And in your case, you'll subsequently open ./UIKit.framework/English.lproj/Localizable.strings (in TextMate). Here you see a wide variety of translations that Apple uses for things like "Print", "OK", "On", "Off", etc. The real magic is that there are about 35 different language translations that you can copy into your own Localizable.strings files, for free.
If you are incredibly brazen and don't mind putting your app's future stability in question, you could skip the whole "copy into your own Localizable.strings" process and go straight to the source programmatically:
NSBundle *uiKitBundle = [NSBundle bundleWithIdentifier:#"com.apple.UIKit"];
NSString *onText = uiKitBundle ? [uiKitBundle localizedStringForKey:#"Yes" value:nil table:nil] : #"YES";
NSString *offText = uiKitBundle ? [uiKitBundle localizedStringForKey:#"No" value:nil table:nil] : #"NO";
Caveat: In no way do I recommend that you actually access these localized resources programmatically in an app that you intend to submit to the App Store. I'm merely illustrating a particular implementation that I've seen which addresses your original question.
Encouraged by Answer to this Question (by Stephan Heilner)
and Answer (by bdunagan) for iPhone/iOS: How can I get a list of localized strings in all the languages my app is localized in?
Objective-C
NSString * LocalizedString(NSString *key) {
return [[NSBundle mainBundle] localizedStringForKey:key];
}
#interface NSBundle(Localization)
- (NSString *)localizedStringForKey:(NSString *)key;
- (NSDictionary<NSString *, NSString *> *)localizationTable;
#end
#implementation NSBundle(Localization)
- (NSDictionary<NSString *, NSString *> *)localizationTable {
NSString *path = [self pathForResource:#"Localizable" ofType:#"strings"];
NSData *data = [NSData dataWithContentsOfFile:path];
NSError *error = nil;
id obj = [NSPropertyListSerialization propertyListWithData:data options:NSPropertyListImmutable format:NULL error:&error];
if (error && obj == nil) {
#throw error;
return nil;
}
if ([obj isKindOfClass:NSDictionary.class]) {
return obj;
}
#throw NSInternalInconsistencyException;
return nil;
}
- (NSString *)localizedStringForKey:(NSString *)key {
return [self localizedStringForKey:key value:nil table:nil];
}
#end
Swift 5
public extension String {
func localized(_ bundle: Bundle = .main) -> String {
bundle.localize(self)
}
var localized: String {
return localized()
}
}
extension Bundle {
static var UIKit: Bundle {
Self(for: UIApplication.self)
}
func localize(_ key: String, table: String? = nil) -> String {
self.localizedString(forKey: key, value: nil, table: nil)
}
var localizableStrings: [String: String]? {
guard let fileURL = url(forResource: "Localizable", withExtension: "strings") else {
return nil
}
do {
let data = try Data(contentsOf: fileURL)
let plist = try PropertyListSerialization.propertyList(from: data, format: .none)
return plist as? [String: String]
} catch {
print(error)
}
return nil
}
}
Usage:
"Photo Library".localized(.UIKit)
To get all keys of localized strings of UIKit:
Bundle.UIKit.localizableStrings?.keys//.map { $0 }
While perhaps not exactly what you were seeking, there is a commercial app in the Mac App Store called "System Strings" claiming that it provides a collection of more than 53000 standard localized strings. It was released November 2, 2012. I am in no way affiliated with this app or the author. The URL is https://itunes.apple.com/us/app/systemstrings/id570467776.
Sounds like what you are asking is if Apple provides a way to access a dictionary of pre-translated strings. I would think that anything provided by Apple for something like this would be located in their Docs: Internationalization Programming Topics
To answer your question I do not believe they provide a dictionary/list of known translations. Either you will have to define them in your Localizable.strings resource file or do as others have stated in the comments and pull the title from the UIBarButtonItem (I would go with the resource file personally).
Why not use base localization with your storyboard? It will localize it for you.
You could use
NSLocalizedString(#"Cancel", #"Cancel")

Display a programmable field several times on the screen

I am a newbie. Can anyone help me?
I have made a function with a programmable input field. I want to call this function several times and display the results at different coordinates on the screen.
What do I wrong and how should I solve it?
// inputName function
NSString* inputName (
int controlX,
int ControlY,
int controlWidth,
int controlHeight,
NSString* myQuestion)
{
// *********************** Inputfield **********************
UITextField *inlogName=[[UITextField alloc] initWithFrame:CGRectMake(controlX,controlY,controlWidth, controlHeight)];
[inlogName setBorderStyle:UITextBorderStyleRoundedRect];
[inlogName setPlaceholder: myQuestion];
[inlogName setDelegate:self]; // HERE I GET AN ERROR ???????
[inlogName text];
NSString *anwser= #"This is a dummy anwser";
[self.view addSubview:inlogName]; // HERE I GET ALSO AN ERROR ??????
return anwser;
}
You have created a C function and have no reference to self in it. I would suggest making it a method on your view controller and invoke it from there.

UISwitches, need an explanation

How do the switches work in XCode4? how do i check which properties are available to me for the switches?
i am trying to check the state of a switch and make a label change according to the state. Something like this:
-(IBAction)clickedOnSwitch:(id)sender {
NSString *switchState = [[NSString alloc]init];
if (mySwitchIsOn) {
switchState = #"switch is On";
}
else
{
switchState = #"switch is Off";
}
myLabel.text = switchState;
[switchState release];
}
Take a look at the UISwitch class reference either on the web or in the Xcode documentation browser.