I'm using ShareKit 2 on an iOS app for version 5 or later. I've got the app configured to use Facebook correctly, and when the action sheet is activated, that's the only option that appears. I want to use the built-in Twitter sharing system, but the option doesn't come up in the action sheet. Here's my code:
SURL *url = [NSURL URLWithString:launchUrl];
SHKItem *item = [SHKItem URL:url title:#"Title" contentType:SHKShareTypeURL];
SHKActionSheet *actionSheet = [SHKActionSheet actionSheetForItem:item];
[SHK setRootViewController:self.view.window.rootViewController];
[actionSheet showInView:self.view.window.rootViewController.view];
As per the instructions. I've got a custom DefaultSHKConfigurator class, in which I suppose these are the relevant methods:
- (NSString*)sharersPlistName {
return #"MySHKSharers.plist";
}
- (NSArray*)defaultFavoriteURLSharers {
return [NSArray arrayWithObjects:#"SHKTwitter",#"SHKFacebook", #"SHKMail", #"SHKTextMessage", nil];
}
Again, only Facebook comes up as an option. For completeness, here's my "MySHKSharers.plist" file (the relevant part):
<dict>
<key>actions</key>
<array>
<string>SHKMail</string>
<string>SHKTextMessage</string>
</array>
<key>services</key>
<array>
<string>SHKTwitter</string>
<string>SHKFacebook</string>
</array>
</dict>
So any ideas why I can't get Twitter, Mail and TextMessage to show up in the action sheet?
If you want to configure the sharers shown in the actionsheet I found this post helpful:https://gist.github.com/1181552
I ran into trouble getting actions (email, text message, etc) to show in the default actionsheet. My solution was to create a custom actionsheet and then to call Sharekit like this:
- (IBAction) getSharingSheet:(id)sender{
UIActionSheet *sheet = [[UIActionSheet alloc] initWithTitle:#"Share"
delegate:self
cancelButtonTitle:#"Cancel"
destructiveButtonTitle:nil
otherButtonTitles:#"Facebook", #"Twitter", #"Save to Photo Library", #"Email", nil];
[sheet showInView:self.view];
}
and then my didDismissWithButtonIndex:
- (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex
{
switch(buttonIndex)
{
case 0:
SHKItem *item = //some item
[SHKFacebook shareItem:item];
break;
case 1:
SHKItem *item = //some item
[SHKTwitter shareItem:item];
break;
case 2:
SHKItem *item = //some item
[SHKPhotoAlbum shareItem:item];
break;
case 3:
SHKItem *item = //some item
[SHKMail shareItem:item];
break;
default:
break;
}
}
Hope this helps!
I had this same issue and just solved it. This may or may not be the same issue that you are facing.
After poking around a bit to see how the action sheet was being built, I found that in SHK.m it was pulling stored favourites out of NSUserDefaults.
+ (NSArray *)favoriteSharersForType:(SHKShareType)type
{
NSArray *favoriteSharers = [[NSUserDefaults standardUserDefaults] objectForKey:[NSString stringWithFormat:#"%#%i", SHKCONFIG(favsPrefixKey), type]];
....
}
These stored results are preferred even over what you've defined as the "default" favourite sharing services. Clearing out these favourites solves the issue.
Related
I have an app that worked great before Apple insisted users be given a choice between "Always" and "When In Use" for Location Manager.
The app used iBeacons to send invitations to play games and accept.
When "Always" is selected the beacons work fine but when I switch to "When In Use" they don't work at all.
I started out using "Always" but change the following code to give users the choice.
In the app's plist I added "Privacy-Location Always and When In Use Usage Descriptions and Privacy-Location When In Use Usage Description" and removed the "Privacy-Location Always Usage Description".
In the app's Delegate I have this
- (void)locationManager:(CLLocationManager *)manager
didChangeAuthorizationStatus:(CLAuthorizationStatus)status{
if([CLLocationManager authorizationStatus]==kCLAuthorizationStatusAuthorizedAlways){
NSLog(#"Always");
AlertView2 = [[UIAlertView alloc] initWithTitle:#"Dual player on two devices is enabled."
message:#"To save battery power go to Settings/Privacy/Location Services and choose \"Never\" when not using I'M GAME. Two people can still play on one device when in \"Never\" mode. To recieve invitations to play only when the app is open select \"When In Use\"."
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[AlertView2 show];
[[NSUserDefaults standardUserDefaults] setObject:#"YES" forKey:#"accessKey"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
if([CLLocationManager authorizationStatus]==kCLAuthorizationStatusAuthorizedWhenInUse){
NSLog(#"WhenInUse");
AlertView2 = [[UIAlertView alloc] initWithTitle:#"Dual player on two devices is enabled."
message:#"To save battery power go to Settings/Privacy/Location Services and choose \"Never\" when not using I'M GAME. Two people can still play on one device when in \"Never\" mode. To recieve invitations to play while app is in background select \"Always\"."
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[AlertView2 show];
[[NSUserDefaults standardUserDefaults] setObject:#"YES" forKey:#"accessKey"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
if([CLLocationManager authorizationStatus]==kCLAuthorizationStatusRestricted){
NSLog(#"restricted");
}
if([CLLocationManager authorizationStatus]==kCLAuthorizationStatusDenied){
NSLog(#"denied");
AlertView2 = [[UIAlertView alloc] initWithTitle:#"Dual player on a single device Only."
message:#"To play on two devices go to Settings Privacy/Location Services and choose \"Always\" or \"When In Use\" for I'M GAME. In \"Always\" you can recieve invites while app is in background, in \"When In Use\" invites only appear when the app is on screen. To preserve battery choose \"Never\" when not using I'M GAME."
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[AlertView2 show];
[[NSUserDefaults standardUserDefaults] setObject:#"YES" forKey:#"accessKey"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
if([CLLocationManager authorizationStatus]==kCLAuthorizationStatusNotDetermined){
NSLog(#"undetermined2");
[locationManager requestAlwaysAuthorization];
[locationManager requestWhenInUseAuthorization];
}
}
Does iBeacon need to have Privacy-Location set to "Always" to work?
So I just found out that in "When In Use" you can't monitor for beacon region is entered or exited only find its range. So I guess the question is how would I use range to send a notification to my user.
When you have your app authorized to do beacon ranging only when it is in the foreground, it's easy to simulate entry/exit logic with just a didRangeBeacons callback.
Set up one class variable:
var beaconLastSeen: Date? = nil
Add this code to your didRangeBeacons method:
if beacons.count > 0 {
if beaconLastSeen == nil {
// call didEnterRegion logic here
}
beaconLastSeen = Date()
}
else {
if beconLastSeen != nil && Date() - beaconLastSeen > 30 {
beaconLastSeen = nil
// call didExitRegion logic here
}
}
You will get an exit event 30 secs after the last beacon detection. You will get an enter event when one is first seen.
EDIT: Here's the same code in Objective C:
NSDate *beaconLastSeen = nil;
...
if (beacons.count > 0) {
if (beaconLastSeen == nil) {
// call didEnterRegion logic here
}
beaconLastSeen = [[NSDate alloc] init];
}
else {
if (beaconLastSeen != nil && [[[NSDate alloc] init] timeIntervalSinceDate: beaconLastSeen] > 30 ) {
beaconLastSeen = nil;
// call didExitRegion logic here
}
}
When copying a URL from chrome on OSX and pasting into an editable WebKit webview, nothing gets pasted.
I verified that there are items on the NSPasteboard and that the NSPasteboardItem has the following types:
"dyn.ah62d4rv4gu8zs3pcnzme2641rf4guzdmsv0gn64uqm10c6xenv61a3k",
"dyn.ah62d4rv4gu8yc6durvwwaznwmuuha2pxsvw0e55bsmwca7d3sbwu",
"public.utf8-plain-text",
"dyn.ah62d4rv4gu8yg55wqzkgc65yqzvg82pwqvdg22p0r73fk8puqyuda8b1gy5xerwdgk2a",
"dyn.ah62d4rv4gu8yg55wqzkgc65yqzvg82pwqvdg22p0r73fk8puqyuda8b1gy5xerwdg3cu"
I understand that these are auto-generated and map to WebURLsWithTitlesPboardType.
On performing the same operation from safari to webview, it works since it only contains
"public.utf8-plain-text"
Is there a known workaround for handling these UTIs better?
Webkit webviews don't seem to support paste operations for dynamic UTIs. I worked around it by recreating the pasteboard items without those UTIs when a paste: was intercepted in webview:doCommandBySelector:
- (void)cleanupPasteboard:(NSPasteboard *)pasteboard {
NSMutableArray *newItems = [[NSMutableArray alloc] init];
for (NSPasteboardItem *item in pasteboard.pasteboardItems) {
NSPasteboardItem *newItem = [[NSPasteboardItem alloc] init];
for (NSString *type in item.types) {
if (![type hasPrefix:#"dyn"]) {
[newItem setData:[item dataForType:type] forType:type];
}
}
[newItems addObject:newItem];
}
[pasteboard clearContents];
[pasteboard writeObjects:newItems];
}
I've recently found out, that I do not receive any EKCalendar objects from EKEventStore in iOS7. In iOS 6.x.x there are no problems with same code snippet. When I'm trying to access defaultCalendarForNewEvents - I do receive a single EKCalendar object (as expected).
I do request access to entity type EKEntityTypeEvent.
The snippet:
__block NSMutableArray *calendarsArray = nil;
if ([self.eventsStore respondsToSelector:#selector(requestAccessToEntityType:completion:)]) {
[self.eventsStore requestAccessToEntityType:EKEntityTypeEvent completion:^(BOOL granted, NSError *error) {
if (granted) {
EKAuthorizationStatus status = [EKEventStore authorizationStatusForEntityType:EKEntityTypeEvent];
if (status == EKAuthorizationStatusAuthorized) {
calendarsArray = [[NSMutableArray alloc] initWithArray:[self.eventsStore calendarsForEntityType:EKEntityMaskEvent]];
}
} else {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error!" message:#"You haven't granted access to calendars. Expected things are not going to happen." delegate:nil cancelButtonTitle:#"I understand" otherButtonTitles:nil];
[alert show];
}
}];
} else {
calendarsArray = [NSMutableArray arrayWithArray:[self.eventsStore calendarsForEntityType:EKEntityTypeEvent]];
}
I do receive 0 objects into calendarsArray. I've also tried to get it by "running through" all EKSources that are of type Local or CalDAV ([source calendarsForEntityType:] - got the same empty (0 object containing) set).
By the way - access to calendars IS granted.
Any suggestions?
After a brief investigation I have found out, that the problem was not in the code. It appears that the problem is in iOS 7.0.3 itself.
After deleting all the sync'ed calendars from the iDevice and adding it back all of the calendars were displayed both within the native Calendar application, and the one I made. After taking this action my code was able to retrieve the calendars from EventStore not depending on the method I would access calendars (via EKSources or EKEventStore itself).
According to the official FAQ from ver.2 to customize your text/content depending on what sharer was selected by the user, you need:
subclass from SHKActionSheet and override
dismissWithClickedButtonIndex
set your new subclass name in
configurator (return it in (Class)SHKActionSheetSubclass;).
It doesn't work for me. But even more: I put
NSLog(#"%#", NSStringFromSelector(_cmd));
in (Class)SHKActionSheetSubclass to see if it's even got called. And it's NOT ;(( So ShareKit doesn't care about this config option...
Has anybody worked with this before?
thank you!
UPD1: I put some code here.
Here's how my subclass ITPShareKitActionSheet looks like. According to the docs I need to override dismissWithClickedButtonIndex:animated:, but to track if my class gets called I also override the actionSheetForItem::
+ (ITPShareKitActionSheet *)actionSheetForItem:(SHKItem *)item
{
NSLog(#"%#", NSStringFromSelector(_cmd));
ITPShareKitActionSheet *as = (ITPShareKitActionSheet *)[super actionSheetForItem:item];
return as;
}
- (void)dismissWithClickedButtonIndex:(NSInteger)buttonIndex animated:(BOOL)animate
{
NSLog(#"%#", NSStringFromSelector(_cmd));
NSString *sharersName = [self buttonTitleAtIndex:buttonIndex];
[self changeItemForService:sharersName];
[super dismissWithClickedButtonIndex:buttonIndex animated:animate];
}
And here's what I do in code to create an action sheet when user presses 'Share' button:
- (IBAction)shareButtonPressed:(id)sender
{
// Create the item to share
SHKItem *item = [SHKItem text:#"test share text"];
// Get the ShareKit action sheet
ITPShareKitActionSheet *actionSheet = [ITPShareKitActionSheet actionSheetForItem:item];
// Display the action sheet
[actionSheet showInView:self.view]; // showFromToolbar:self.navigationController.toolbar];
}
When I run this code, press 'Share' button and select any sharer I expect to get two lines in log:
actionSheetForItem: - custom action sheet got created
dismissWithClickedButtonIndex:animated: - custom mechanics to
process action sheet's pressed button got called.
But for some reason I get only the first line logged.
I was having the same issues but I've suddenly got it to call my Subclass successfully.
Firstly My Configurator is setup as so:
-(Class) SHKActionSheetSubclass{
return NSClassFromString(#"TBRSHKActionSheet");
}
Now My Subclass:
.h File
#import "SHKActionSheet.h"
#interface TBRSHKActionSheet : SHKActionSheet
#end
.m implementation override:
#import "TBRSHKActionSheet.h"
#import "SHKActionSheet.h"
#import "SHKShareMenu.h"
#import "SHK.h"
#import "SHKConfiguration.h"
#import "SHKSharer.h"
#import "SHKShareItemDelegate.h"
#implementation TBRSHKActionSheet
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
+ (SHKActionSheet *)actionSheetForItem:(SHKItem *)i
{
NSLog(#"%#", NSStringFromSelector(_cmd));
SHKActionSheet *as = [self actionSheetForType:i.shareType];
as.item = i;
return as;
}
- (void)dismissWithClickedButtonIndex:(NSInteger)buttonIndex animated:(BOOL)animated
{
NSInteger numberOfSharers = (NSInteger) [sharers count];
// Sharers
if (buttonIndex >= 0 && buttonIndex < numberOfSharers)
{
bool doShare = YES;
SHKSharer* sharer = [[NSClassFromString([sharers objectAtIndex:buttonIndex]) alloc] init];
[sharer loadItem:item];
if (shareDelegate != nil && [shareDelegate respondsToSelector:#selector(aboutToShareItem:withSharer:)])
{
doShare = [shareDelegate aboutToShareItem:item withSharer:sharer];
}
if(doShare)
[sharer share];
}
// More
else if ([SHKCONFIG(showActionSheetMoreButton) boolValue] && buttonIndex == numberOfSharers)
{
SHKShareMenu *shareMenu = [[SHKCONFIG(SHKShareMenuSubclass) alloc] initWithStyle:UITableViewStyleGrouped];
shareMenu.shareDelegate = shareDelegate;
shareMenu.item = item;
[[SHK currentHelper] showViewController:shareMenu];
}
[super dismissWithClickedButtonIndex:buttonIndex animated:animated];
}
Finally on my implementation file I've not modified the call to SHKActionSheet as Vilem has suggested because of some dependancies that seemed to cause conflicts for me.
So this is my caller (straight from tutorial):
NSURL *url = [NSURL URLWithString:#"http://getsharekit.com"];
SHKItem *item = [SHKItem URL:url title:#"ShareKit is Awesome!" contentType:SHKURLContentTypeWebpage];
// Get the ShareKit action sheet
SHKActionSheet *actionSheet = [SHKActionSheet actionSheetForItem:item];
// ShareKit detects top view controller (the one intended to present ShareKit UI) automatically,
// but sometimes it may not find one. To be safe, set it explicitly
[SHK setRootViewController:self];
// Display the action sheet
[actionSheet showFromToolbar:self.navigationController.toolbar];
This Calls no problems for me.
edit: by far the best way to achieve this is to use SHKShareItemDelegate. More info is in ShareKit's FAQ.
I have 2 pages with xib files. One is a login page and the other is a welcome page.
Here is the login function:
-(IBAction)login{
NSLog(#"login begin");
NSString *loginResponse = [self tryLogin];
NSLog(#"%#", loginResponse);
loginError.text = loginResponse;
NSUserDefaults *session = [NSUserDefaults standardUserDefaults];
[session setInteger:1 forKey:#"islogged"];
}
After the user is logged in successfully I need to redirect them to another xib file which is called Notifications. Now how exactly should I do this?
Also for some reason when I put an if statement inside the function, it doesnt go into the statements.
-(IBAction)login{
NSLog(#"login begin");
NSString *loginResponse = [self tryLogin];
NSLog(#"%#", loginResponse);
if(responseInt == #"2"){
NSLog(#"login error 2");
UIAlertView *alertURL = [[UIAlertView alloc] initWithTitle:#"Error!"
message:#"Username or password is wrong!"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles: nil];
[alertURL show];
[alertURL release];
///////////////////////////////////////////////////
}else if(responseInt == #"0"){
NSUserDefaults *session = [NSUserDefaults standardUserDefaults];
[session setInteger:1 forKey:#"islogged"];
}
}
As you can see I try to nslog something as soon as one of the conditions are met but it does not go into any of the statements (the loginResponse is sent back as 2).
How to change views
If you app is navigation-based then you can add new fullscreen views using method [self.navigationController pushViewController:controller animated:YES];.
Everywhere you can add new fullscreen view using next method : [self presentModalViewController:controller animated:YES];.
Issue with 'if-statement'.
When you want to compare to objects you should use one of following methods:
compare:, this method return one of following values: NSOrderedSame, NSOrderedDescending, NSOrderedAscending. For example, if ([responseInt compare:#"2"] == NSOrderedSame) {// equal}
'isEqual:', for example, if ([responseInt isEqual:#"2"]) {// equal}
or some specific comparators for concrete class
Don't forget that when you are comparing using == or > and etc you are comparing addresses of objects in memory.
Have you tried doing isEqualToString: instead of the equal sign? That is a more reliable string comparator. Also it seems strange that your login method is returning a string and not an integer. Is there a reason for that?