How to customize ShareKit actionsheet? - iphone

I am using ShareKit (www.getsharekit.com) to share my URL's to Twitter and Facebook. I want to be able to remove all of the additional social network it points to, but am not sure where to edit?

in SHK.m find this method
+ (NSArray *)favoriteSharersForType:(SHKShareType)type
and change
switch (type)
{
case SHKShareTypeURL:
favoriteSharers = [NSArray arrayWithObjects:#"SHKTwitter",#"SHKFacebook",#"SHKReadItLater",nil];
break;
case SHKShareTypeImage:
favoriteSharers = [NSArray arrayWithObjects:#"SHKTwitter",#"SHKFacebook",#"SHKReadItLater",nil];
break;
case SHKShareTypeText:
favoriteSharers = [NSArray arrayWithObjects:#"SHKTwitter",#"SHKFacebook",#"SHKReadItLater",nil];
break;
case SHKShareTypeFile:
favoriteSharers = [NSArray arrayWithObjects:#"SHKTwitter",#"SHKFacebook",#"SHKReadItLater",nil];
break;
to the following for each instance of the switch statement:
favoriteSharers = [NSArray arrayWithObjects:#"SHKFacebook", nil];
or what ever other options you want to support (ie if you only want twitter and facebook add #"SHKTwitter", to the array)
that will eliminate the other options but the action sheet that displays the options wont reflect the change and it will still give the more option, which we also need to disable.
So to do that go to SHKActionSheet.m
in this method you can change the title from "Share" to something more specific (this part is optional) ie "Share with Facebook and Twitter"
+ (SHKActionSheet *)actionSheetForType:(SHKShareType)type
change
SHKActionSheet *as = [[SHKActionSheet alloc] initWithTitle:SHKLocalizedString(#"PUT YOUR NEW TITLE HERE")
delegate:self
cancelButtonTitle:nil
destructiveButtonTitle:nil
otherButtonTitles:nil];
as.item = [[[SHKItem alloc] init] autorelease];
as.item.shareType = type;
than in that same method, delete this line
// Add More button
[as addButtonWithTitle:SHKLocalizedString(#"More...")];
that will remove the more button but now the code will now confuse the more button with the cancel button, so to fix that, go to this method:
- (void)dismissWithClickedButtonIndex:(NSInteger)buttonIndex animated:(BOOL)animated
and delete the following else if statement
// More
else if (buttonIndex == sharers.count)
{
SHKShareMenu *shareMenu = [[SHKCustomShareMenu alloc] initWithStyle:UITableViewStyleGrouped];
shareMenu.item = item;
[[SHK currentHelper] showViewController:shareMenu];
[shareMenu release];
}
what this method is susposted to do is take the button that is normally the more button and open the more options. So by deleting it the code has no associated action with the cancel button so it just closes and release the action sheet, effectively creating a cancel button

From ShareKit/Core/SHK.m:
[SHK setFavorites: (NSArray *)favs forType:(SHKShareType)type]
+ (void)setFavorites:(NSArray *)favs forType:(SHKShareType)type
{
[[NSUserDefaults standardUserDefaults] setObject:favs forKey:[NSString stringWithFormat:#"%#%i", SHK_FAVS_PREFIX_KEY, type]];
}
favs is like :
[NSArray arrayWithObjects:#"SHKTwitter",#"SHKFacebook",#"SHKReadItLater",nil]
type is SHKShareType:
typedef enum
{
SHKShareTypeUndefined,
SHKShareTypeURL,
SHKShareTypeText,
SHKShareTypeImage,
SHKShareTypeFile
} SHKShareType;

The new way to do this with latest version of ShareKit 2.0 is to overwrite the following methods in your SHKConfigurator (extending DefaultSHKConfigurator.m)
// SHKActionSheet settings
- (NSNumber*)showActionSheetMoreButton {
return [NSNumber numberWithBool:true];// Setting this to true will show More... button in SHKActionSheet, setting to false will leave the button out.
}
/*
Favorite Sharers
----------------
These values are used to define the default favorite sharers appearing on ShareKit's action sheet.
*/
- (NSArray*)defaultFavoriteURLSharers {
return [NSArray arrayWithObjects:#"SHKTwitter",#"SHKFacebook", #"SHKReadItLater", nil];
}
- (NSArray*)defaultFavoriteImageSharers {
return [NSArray arrayWithObjects:#"SHKMail",#"SHKFacebook", #"SHKCopy", nil];
}
- (NSArray*)defaultFavoriteTextSharers {
return [NSArray arrayWithObjects:#"SHKMail",#"SHKTwitter",#"SHKFacebook", nil];
}

See: http://github.com/ideashower/ShareKit/issues/closed#issue/30

For the unwanted services not to appear on the action sheet you can simply remove the respective sharer services classes from the project.
Go to project--> Sharers --> Services --> select the .h and .m files of the respective service and delete it.
For example Facebook option can be removed by deleting SHKFacebook.h and SHKFacebook.m.
Note: You will have to check the class import to avoid errors.

See my answer on the other thread with more thorough explanation.
It is now easily configurable without changing ShareKit's code, if you use ShareKit 2.0
Basically, if you use only Facebook and Twitter, it is easier not to mess with ShareKit's code. You can easily
create your own UIActionSheet with two buttons, and call ShareKit's convenience share methods.

Easiest way is to edit "SHKSharers.plist" and keep the services you need.

Related

Initial text and paperclipped-URL for action in UIActivityViewController & UIActivityItemSource?

Finally been making it through Apple's (rather dismal) documentation on the new UIActivityViewController class and the UIActivityItemSource protocol, and I'm trying to send different data sets to different actions called from the activity view. To simplify things, I'm looking at two things.
A Facebook posting action, which should say "Check this out!" and also attach a URL to the post (with that cute little paperclip).
A Twitter posting action, which should say "Check this out, with #hashtag!" and also attach that same URL (with the same paperclip).
Here's the code I've got implemented right now.
- (id)activityViewController:(UIActivityViewController *)activityViewController itemForActivityType:(NSString *)activityType {
if ([activityType isEqualToString:UIActivityTypePostToFacebook]) {
return #"Check this out!";
} else if ([activityType isEqualToString:UIActivityTypePostToTwitter]) {
return #"Check this out, with #hashtag!";
}
return #"";
}
- (id)activityViewControllerPlaceholderItem:(UIActivityViewController *)activityViewController {
return #"";
}
And then when I set up this activity view controller (it's in the same class), this is what I do.
UIActivityViewController *activityView = [[UIActivityViewController alloc] initWithActivityItems:#[self] applicationActivities:nil];
[self presentViewController:activityView animated:YES completion:nil];
My dilemma is how to attach that NSURL object. It's relatively easy when calling the iOS 6 SL-class posting modals; you just call the individual methods to attach a URL or an image. How would I go about doing this here?
I'll note that instead of returning NSString objects from -activityViewController:itemForActivityType, if I return just NSURL objects, they show up with that paperclip, with no body text in the post. If I return an array of those two items, nothing shows up at all.
Evidently it was as simple as this: passing in an array to the first argument of UIActivityViewController's init call, with each item in the array handling a different data type that will end up in the compose screen. self handles the text, and the second object (the NSURL) attaches the URL.
NSArray *items = #[self, [NSURL URLWithString:#"http://this-is-a-url.com"]];
UIActivityViewController *activityView = [[UIActivityViewController alloc] initWithActivityItems:items applicationActivities:nil];
[self presentViewController:activityView animated:YES completion:nil];
Really wish there was more on this, but here it is.

Adding Speech to custom UIMenuController

I created a custom UIMenuController in a UIWebView but it seems to get rid of the "Speak Selection" option in the UIMenuController after that. The speak selection option is turned on in Preferences on all test devices and it appears in other apps, including non-Apple apps. Is there an accessibility service or part of the sharedMenuController that I can call to get this item?
UIMenuItem *copyMenuItem = [[UIMenuItem alloc] initWithTitle:NSLocalizedString(#"Copy", #"Copy menu item") action:#selector(myappCopy:)];
UIMenuItem *highlightMenuItem = [[UIMenuItem alloc] initWithTitle:NSLocalizedString(#"Highlight", #"Highlight menu option") action:#selector(myappHighlight:)];
UIMenuItem *unhighlightMenuItem = [[UIMenuItem alloc] initWithTitle:NSLocalizedString(#"Remove Highlight", #"Remove Highlight menu option")
action:#selector(myappRemoveHighlight:)];
UIMenuItem *noteMenuItem = [[UIMenuItem alloc] initWithTitle:NSLocalizedString(#"Note", #"Note menu options") action:#selector(myappNote:)];
[UIMenuController sharedMenuController].menuItems = [NSArray arrayWithObjects:copyMenuItem, highlightMenuItem, unhighlightMenuItem, noteMenuItem, nil];
[copyMenuItem release];
[highlightMenuItem release];
[unhighlightMenuItem release];
[noteMenuItem release];
I even tried to parse the existing shared menu items at the start, but I don't see anything dumped in the log. The method is getting called on app launch.
Tried this at top of method:
for (UIMenuItem *menuItem in [UIMenuController sharedMenuController].menuItems) {
NSLog(#"title: %#", menuItem.title);
NSLog(#"action: %#", menuItem.action);
}
Any help is much appreciated! Thanks - Eric
I've found some interesting things with this bug. Basically, when speak selection is enabled, after you make the first selection the UIMenuController is emptied of menuItems. The solution, though hacky, is simple.
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
NSString *selectorString = NSStringFromSelector(action);
BOOL isAccessibilitySelector = [selectorString isEqualToString:#"_accessibilitySpeak:"] || [selectorString isEqualToString:#"_accessibilityPauseSpeaking:"];
if (isAccessibilitySelector && [super canPerformAction:action withSender:sender]) {
//(re)add menuItems to UIMenuController
return YES;
}
return NO;
}
I should note that you must re-add the menuItems after their canPerformAction...() has been called.
I have submitted this as radar:12931434. Update: DUP'ed to 13060693.
Some of the UIMenuController items can be found in UIResponder.h in UIKit framework.
#interface NSObject(UIResponderStandardEditActions) // these methods are not implemented in NSObject
- (void)cut:(id)sender __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_3_0);
- (void)copy:(id)sender __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_3_0);
- (void)paste:(id)sender __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_3_0);
- (void)select:(id)sender __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_3_0);
- (void)selectAll:(id)sender __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_3_0);
- (void)delete:(id)sender __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_3_2);
- (void)makeTextWritingDirectionLeftToRight:(id)sender __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_5_0);
- (void)makeTextWritingDirectionRightToLeft:(id)sender __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_5_0);
#end
But there is no speak text option there. It turns out if you override "canPerformAction: WithSelector:", within a subclass of your UIWebView or UITextField as listed below, you will also get a listing of all of the actions sent to self including the UIMenuController options.
// Override
- (BOOL) canPerformAction:(SEL)action withSender:(id)sender
{
NSLog(#"%#",NSStringFromSelector(action));
//if you are customizing your menu, return NO except for your specific selectors
return YES;
}
You'll find several methods that may interest you, including _accessibilitySpeak: and _accessibilityPauseSpeaking: and _define: (please note these three selectors are iOS 5 only). The underscore means that they are private, so also keep in mind that you can't directly call them with the classic [class selector] syntax.
Remember, these are system menuItems, which means Apple will stick them in front of any menu items you add, often leaving your menu items in a second layer accessed by tapping the > arrow. If you want to control the order in which the items are display, and/or mix Apple's system items with your items, you will need to create custom menu items for these actions that call a method in your class like this:
- (void) myAppSpeak: (UIMenuController*) sender
{
[super performSelector:#selector(_accessibilitySpeak:)];
}
Keep in mind that these methods need to be implemented in a subclass of a class that implements these already, such as a sub class of UIWebView....not a subclass of UIWebViewController.
Then inside the controller, or wherever you build your UIMenuController, create the custom button that calls this method. Be sure if you are in a web view, you are referencing an object of type of your subclass, and not the generic webview. Otherwise, it won't work.
UIMenuItem *speakMenuItem = [[UIMenuItem alloc] initWithTitle:#"Speak" action:#selector(myAppSpeak:)];
[UIMenuController sharedMenuController].menuItems = [NSArray arrayWithObjects:speakMenuItem, etc. etc., nil];
Even though you are adding it to the your menu items, it will not appear unless you return YES for the selector in your canPerformAction: WithSelector: in your subclass of your web view or text field. So feel free to add items here that may be circumstantial otherwise. You can use logic in your subclassed view to sort that out.

Activity Indicator IOS usage question

First I would like to thank everyone who attempts to help me with my problem, I am new to iOS development, specifically objective-c, so I apologize if my question is extremely obvious.
I am making an app that loads some new data (parsed from a website but it is NOT a UIWebView) into the same current view every time the user changes a selection on the picker view (UIPickerView). The method that inserts this new data into the current view is called -(IBAction) getNewData: (id) sender.
getNewData is called every time the user makes a new selection with the picker. This is how it is called within the picker method and the picker as well as everything else works fine.
- (void)pickerView:(UIPickerView*)pickerView
didSelectRow:(NSInteger)row
inComponent:(NSInteger)component
{
NSString *choosen;
choosen = [currentLottoNames objectAtIndex:row];
[self getNewData:choosen];
}
I would like to implement an activity indicator (spinner) for the loading time in-between the time the user makes/changes his selection on the scroller to the time the actual data shows up in the view after the user has made his selection on the scroller. How would I go about implementing this?
Thank you.
To show the user that you are accessing information via apples built in status bar you use
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
If you would like to display like a pop up message you need to declare in the header a
UIAlertView *load_message;
and then when you would like to show the load_message use
load_message = [[UIAlertView alloc] initWithTitle:#"Loading..." message:nil delegate:self cancelButtonTitle:nil otherButtonTitles:nil];
[load_message show];
UIActivityIndicatorView *active = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
active.center = CGPointMake(load_message.bounds.size.width / 2, load_message.bounds.size.height - 40);
[active startAnimating];
[load_message addSubview:active];
and that will show a pop up displayed to the user that you are loading something. This is for locking up the screen showing the user that you are getting some information.

UIActionSheet in Landscape has incorrect buttonIndicies

I have an action sheet that is causing me grief on the iphone in Landscape orientation. Everything displays just fine, but in Landscape, the first real button has the same index as the cancel button and so the logic doesn't work.
I've tried creating the actionSheet using initWithTitle: delegate: cancelButtonTitle: destructiveButtonTitle: otherButtonTitles: but that was just the same, my current code is as follows;
UIActionSheet* actionMenu = [[UIActionSheet alloc] init];
actionMenu.delegate = self;
actionMenu.title = folderentry.Name;
actionMenu.cancelButtonIndex = 0;
[actionMenu addButtonWithTitle:NSLocalizedString(#"str.menu.cancel",nil)];
[self addActiveButtons:actionMenu forEntry:folderentry];
[actionMenu showInView:[self.navigationController view]];
[actionMenu release];
The addActiveButtons method basically configures which buttons to add which it does using code like this;
[menu addButtonWithTitle:NSLocalizedString(#"str.menu.sendbyemail",nil)];
There are perhaps 6 buttons at times so in landscape mode the actionSheet gets displayed like this;
My delegate responds like this;
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
NSLog(#"Cancel Button Index is : %d",actionSheet.cancelButtonIndex);
NSLog(#"Button clicked was for index : %d",buttonIndex);
NSString *command = [actionSheet buttonTitleAtIndex:buttonIndex];
DLog(#"COMMAND IS: %# for index: %d",command,buttonIndex);
if ([command isEqualToString:NSLocalizedString(#"str.menu.sendbyemail",nil)]) {
// Do stuff here
}
if ( ... similar blocks ... ) { }
}
In the example shown, I am finding that cancelButtonIndex is 0 as expected, but so is the button index for the first other button! This means if I click on the second (Save to Photos) button for example, my debug output looks like this;
Cancel Button Index is : 0
Button clicked was for index : 1
COMMAND IS: Send by Email for index: 1
I've tried various permutations and am now tearing my hair out wondering what I'm missing. I've had a good search around but the other problems people seem to be having are display issues, rather than functionality ones.
Can anyone see where I've gone wrong?
PS. I know this isn't the greatest UI experience, but I figure that most users will actually be in portrait most of the time or using the iPad version of the app so I'm prepared to accept the actionsheet default behaviour for landscape assuming I can get it to actually work!
OK, fixed it by counting how many buttons I was adding and then adding the cancel button as the last option, so my code looks like this;
int added = [self addActiveButtons:actionMenu forEntry:folderentry];
[actionMenu addButtonWithTitle:NSLocalizedString(#"str.menu.cancel",nil)];
actionMenu.cancelButtonIndex = added;
Hope that helps someone else struggling witht the same issue!
I ran into the same issue even though I already was including the Cancel Button as the last one in the action sheet and setting its index accordingly. My problems had to do with the 'Destructive' button. After some investigation, here is my take on the problem:
After N buttons have been added to the actionsheet, it switches it's layout to put the Destructive button at the top and the Cancel button at the button. In between is a scrollable view that includes all of the other buttons. Other sources indicate that this is a a table view.
For the iPhone, N is 7 for Portrait orientation and 5 for Landscape orientation. Those numbers are for all buttons including Cancel and Destructive.
It does not matter where in the action sheet you had originally put the Cancel and Destructive buttons within the action sheet. Once the limit has been reached, the Destructive button is moved to the top and the Cancel is moved to the bottom.
The problem is that the indices are not adjusted accordingly. So, if you did not initially add the Cancel as the last button and the Destructive as the first, the wrong index will be reported in actionSheet:clickedButtonAtIndex: as the initial report stated.
So, if you are going to have more than N buttons in your action sheet you MUST add the Destructive button to the actionSheet as the first button to the action sheet. You MUST add the Cancel button as the last button added to the action sheet. When initially constructing the sheet just leave both as nil, as described in another answer.
I had the same problem. To fix it, I just create an actionSheet with nil for all the buttons, and added buttons manually afterwards. Lastly, in the handler, ignore the firstOtherButtonIndex because it will be wrong (even if you set it ahead of time). Instead, assume that it is 1 because index 0 is the cancel button in this example. Here's the code:
NSArray *items = [NSArray arrayWithObjects:#"one", #"two", #"three", nil];
UIActionSheet* actionSheet = [[[UIActionSheet alloc] initWithTitle:#"Title" delegate:self cancelButtonTitle:nil destructiveButtonTitle:nil otherButtonTitles:nil] autorelease];
[actionSheet addButtonWithTitle:#"Cancel"];
for (NSString *title in items) {
[actionSheet addButtonWithTitle:title];
}
[actionSheet addButtonWithTitle:#"Destroy"];
// set these if you like, but don't bother setting firstOtherButtonIndex.
actionSheet.cancelButtonIndex = 0;
actionSheet.destructiveButtonIndex = [items count]+1;
Also, don't forget to show this from a tab view if you're on an iPhone because the tab bar steals touch events and prevents the lower button from being hit.
My solution is to initialize like this specifying only the destructiveButtonTitle...
UIActionSheet * as =[[[UIActionSheet alloc] initWithTitle:nil
delegate:self
cancelButtonTitle:nil
destructiveButtonTitle:#"Cancel"
otherButtonTitles:nil] autorelease];
[as addButtonWithTitle:#"Button 1"];
[as addButtonWithTitle:#"Button 2"];
That way you get the Cancel button at index 0 always and your own buttons begin at index 1 even when there is a scroll view.

disable Alert view button

I have an alert view for twitter posting.
Alert view has 2 button and a textfield
send and cancel
I want to disable send button, until user fills the message box(i.e textfield).
like,empty field kind of validation.
How can I disable send button?
I had a similar requirement and was able to do this without resorting to anything explicitly prohibited by Apple (ie, the use of private classes or API's). In the example below, I find and then disable the "Recover" button.
Note #1 -- The placement of "[alert Show]" is important. It (apparently) lays out the views, so must be done before attempting to look through the view hierarchy.
Note #2 -- the "contains:" method is one I defined that does an NSString case-insensitive substring search. Use rangeOfString perhaps in your code.
UIAlertView* alert = [[UIAlertView alloc] initWithTitle:#"Application Warning"
message:#"What should I do with the file?"
delegate:self
cancelButtonTitle:#"Ignore"
otherButtonTitles:#"Remove", #"Recover", nil];
[alert show];
// try to find and disable "Recover" button
for(UIView *aView in alert.subviews)
{
if ([[[aView class] description] contains:#"Button"])
{
UIButton *aButton = (UIButton *)aView;
if ([aButton.titleLabel.text contains:#"Recover"])
{
aButton.enabled = NO;
}
}
}
This is not possible with the current SDK. You will have to create a custom view to take the user's input. The fact you are adding a textfield to the UIAlertView is itself unsupported and could break in any future SDK anyway.
I would suggest you create a custom view and if you still want it to look like a UIAlertView you can do this with appropriate images and custom buttons.