I execute the following code to let the user choose multiple calendars to use for my notepad app. Until iOS 10.3.1, there was no problem. On 11.0.2, it was still working on actural devices. However, since 11.1 it crashes with the following error.
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSDictionaryM setObject:forKeyedSubscript:]: key cannot be nil'
The code is as follows. Basically, I'm opening a blank calendar chooser.
if (_eventStore == nil) {
_eventStore = [[EKEventStore alloc] init];
}
// the selector is available, so we must be on iOS 6 or newer
[_eventStore requestAccessToEntityType:EKEntityTypeEvent
completion:^(BOOL granted, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (error)
{
// display error message here
}
else if (!granted)
{
// display access denied error message here
}
else
{
// access granted
EKCalendarChooser *calendarChooser = [[EKCalendarChooser alloc]
initWithSelectionStyle:EKCalendarChooserSelectionStyleMultiple
displayStyle:EKCalendarChooserDisplayAllCalendars
eventStore:_eventStore];
calendarChooser.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
calendarChooser.delegate = self;
calendarChooser.showsCancelButton = YES;
calendarChooser.showsDoneButton = YES;
NSSet *calendarSet = [[NSSet alloc] init];
calendarChooser.selectedCalendars = calendarSet;
UINavigationController *sub = [[UINavigationController alloc] initWithRootViewController:calendarChooser];
sub.navigationBar.barStyle = UIBarStyleDefault;
sub.toolbar.barStyle = UIBarStyleDefault;
[self presentViewController:sub animated:YES completion:nil];
//ios11 crashes after this
}
});
}];
Thanks for your help.
It turns out that EKCalendarChooserDisplayAllCalendars was causing the crash. Although it's not ideal, now I can avoid the crash when iOS is 11.1 or higher.
EKCalendarChooserDisplayStyle displayStyle = EKCalendarChooserDisplayAllCalendars;
if (#available(iOS 11.1, *)) {
displayStyle = EKCalendarChooserDisplayWritableCalendarsOnly;
}
EKCalendarChooser *calendarChooser = [[EKCalendarChooser alloc]
initWithSelectionStyle:EKCalendarChooserSelectionStyleMultiple
displayStyle:displayStyle
eventStore:eventStore];
Related
i have no idea why the uiactivityviewcontroller crash when i use the prensentViewController method.
it is weird, anyone has any clue?
the program is running fine not untile when i use the presenviewcontroller.
#import "ActivityViewController.h"
#interface ActivityViewController ()
#end
#implementation ActivityViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
[self createTextField];
[self createButton];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void) createTextField
{
self.myTextField = [[UITextField alloc] initWithFrame:CGRectMake(20.0f, 35.0f, 280.0f, 30.0f)];
self.myTextField.translatesAutoresizingMaskIntoConstraints = NO;
self.myTextField.borderStyle = UITextBorderStyleRoundedRect;
self.myTextField.placeholder =#"Enter text to Share";
self.myTextField.delegate = self;
[self.view addSubview:self.myTextField];
}
- (void) createButton
{
self.myButtonShare = [UIButton buttonWithType:UIButtonTypeRoundedRect];
self.myButtonShare.frame = CGRectMake(20.0f, 80.0f, 280.0f, 40.0f);
self.myButtonShare.translatesAutoresizingMaskIntoConstraints = NO;
[self.myButtonShare setTitle:#"Share" forState:UIControlStateNormal];
[self.myButtonShare addTarget:self action:#selector(handleShare:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:self.myButtonShare];
}
- (void) handleShare:(id)sender
{
NSArray *activities = #[UIActivityTypeMail,UIActivityTypeMessage];
self.myActivityViewController = [[UIActivityViewController alloc] initWithActivityItems:#[self.myTextField.text] applicationActivities:activities];
[self presentViewController:self.myActivityViewController animated:YES completion:nil];
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
[self.myButtonShare resignFirstResponder];
return YES;
}
What ccjensen said is all correct, except you can actually restrict your share sheet to just Mail & Message by excluding activities, this way for example:
activityController.excludedActivityTypes = #[ UIActivityTypeCopyToPasteboard, UIActivityTypeAddToReadingList ];
This would only show the email sharing by default.
The documentation for UIActivityViewController's initWithActivityItems:applicationActivities: states that the NSArray passed as the second parameter (applicationActivities) should be an "array of UIActivity". You are passing in an array containing the two objects UIActivityTypeMail and UIActivityTypeMessage which are of type NSString *const. It seems that you were hoping that you could restrict the share sheet to only show the mail and messages activity, which is currently not possible.
To stop the crash from happening, change your code to:
<...>
self.myActivityViewController = [[UIActivityViewController alloc] initWithActivityItems:#[self.myTextField.text] applicationActivities:nil];
<...>
I develop an application and I need to display the content using an UITextView which must have set the height dynamically and it must recognize a link.
I used code above:
self.textView.text = [NSString stringWithFormat:#"%# \n %#", self.offersObjects.body, self.offersObjects.url];
self.textView.dataDetectorTypes = UIDataDetectorTypeLink;
if (([[[UIDevice currentDevice] systemVersion] integerValue] < 7)){
CGRect frame = self.textView.frame;
frame.size.height = self.textView.contentSize.height;contentSize.height;
self.textView.frame = frame;
}else{
[self.textView sizeToFit];
[self.textView layoutIfNeeded];
}
My problem is that it doesn't recognize the link .
try with below code :
-(IBAction)txtStustes:(id)sender
{
NSError *error = nil;
NSDataDetector *detector = [NSDataDetector dataDetectorWithTypes:NSTextCheckingTypeLink
| NSTextCheckingTypePhoneNumber error:&error];
NSString *string = self.textView.text;
NSArray *matches = [detector matchesInString:string options:0 range:NSMakeRange(0, [string length])];
for (NSTextCheckingResult *match in matches) {
if ([match resultType] == NSTextCheckingTypeLink) {
NSURL *url = [match URL];
[[UIApplication sharedApplication] openURL:url];
}
}
}
Also add below code in your viewDidLoad method
UITapGestureRecognizer *LblProfileNameTouch=[[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(txtStustes:)];
[LblProfileNameTouch setNumberOfTouchesRequired:1];
[self.textView addGestureRecognizer:LblProfileNameTouch];
I am trying to save NSMutableDictionary in iOS keychain using KeychainItemWrapper classes. But I am not able to save it. I am getting error
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Couldn't add the Keychain Item.'
Here is my data to be saved
{
country = USA;
id = 3;
name = "Test User";
photo = "http://www.mydomain.com/images/user1.jpg";
result = true;
"country" = 1;
}
Here is my code
// Call to save
[self storeLoggedInUserInfoInKeychainWithDictionary:dict];
-(void)storeLoggedInUserInfoInKeychainWithDictionary:(NSMutableDictionary*)dict
{
// Save Login Credentials
KeychainItemWrapper* loginUserkeychain = [[KeychainItemWrapper alloc] initWithIdentifier:LOGIN_USER_SERVICE accessGroup:nil];
NSString *error;
[loginUserkeychain setObject:(__bridge id)(kSecAttrAccessibleWhenUnlocked) forKey:(__bridge id)(kSecAttrAccessible)];
NSData *dictionaryRep = [NSPropertyListSerialization dataFromPropertyList:dict format:NSPropertyListXMLFormat_v1_0 errorDescription:&error];
[loginUserkeychain setObject:dictionaryRep forKey:(__bridge id)(kSecValueData)];
}
-(NSMutableDictionary*)fetchLoggedInUserInfoFromKeychain
{
KeychainItemWrapper* loginUserkeychain = [[KeychainItemWrapper alloc] initWithIdentifier:LOGIN_USER_SERVICE accessGroup:nil];
NSString *error;
//When the NSData object object is retrieved from the Keychain, you convert it back to NSDictionary type
NSData *dictionaryRep = [loginUserkeychain objectForKey:(__bridge id)(kSecValueData)];
NSDictionary *dictionary = [NSPropertyListSerialization propertyListFromData:dictionaryRep mutabilityOption:NSPropertyListImmutable format:nil errorDescription:&error];
if (error) {
NSLog(#"%#", error);
}
return [NSMutableDictionary dictionaryWithDictionary:dictionary];
}
-(void)resetLoggedInUserInfoFromKeychain
{
KeychainItemWrapper* loginUserkeychain = [[KeychainItemWrapper alloc] initWithIdentifier:LOGIN_USER_SERVICE accessGroup:nil];
[loginUserkeychain resetKeychainItem];
}
Can anybody tell me whats wrong in above code ?
Thanks in advance.
After few attempts & research using below code I am able to save the data in keychain. If any one is interested can have a look at below code
-(void)storeLoggedInUserInfoInKeychainWithDictionary:(NSMutableDictionary*)dict
{
// Create encoded data
NSData *encodedData= [NSKeyedArchiver archivedDataWithRootObject:dict];
// Create encoded string from data
NSString *encodedString= [encodedData base64EncodedString];
// Save Login Credentials
KeychainItemWrapper* tranxkeychain = [[KeychainItemWrapper alloc] initWithIdentifier:LOGIN_USER_KEYCHAIN accessGroup:nil];
[tranxkeychain setObject:(__bridge id)(kSecAttrAccessibleWhenUnlocked) forKey:(__bridge id)(kSecAttrAccessible)];
[tranxkeychain setObject:LOGIN_USER_SERVICE forKey: (__bridge id)kSecAttrService];
[tranxkeychain setObject:LOGIN_USER_INFO forKey:(__bridge id)(kSecAttrAccount)];
[tranxkeychain setObject:encodedString forKey:(__bridge id)(kSecValueData)];
}
-(NSDictionary*)fetchLoggedInUserInfoFromKeychain
{
KeychainItemWrapper* tranxkeychain = [[KeychainItemWrapper alloc] initWithIdentifier:LOGIN_USER_KEYCHAIN accessGroup:nil];
[tranxkeychain setObject:(__bridge id)(kSecAttrAccessibleWhenUnlocked) forKey:(__bridge id)(kSecAttrAccessible)];
[tranxkeychain setObject:LOGIN_USER_SERVICE forKey: (__bridge id)kSecAttrService];
// Get decoded string
NSString *decodedString=[tranxkeychain objectForKey:(__bridge id)(kSecValueData)];
// Get decoded data
NSData *decodedData= [NSData dataFromBase64String:decodedString];
NSDictionary *dict =[NSKeyedUnarchiver unarchiveObjectWithData:decodedData];
return dict;
}
-(void)resetLoggedInUserInfoFromKeychain
{
KeychainItemWrapper* tranxkeychain = [[KeychainItemWrapper alloc] initWithIdentifier:LOGIN_USER_KEYCHAIN accessGroup:nil];
[tranxkeychain resetKeychainItem];
}
Using iOS6:
I would like to retrieve the text entered by a user into a UITextField associated with the UIAlertView. I am aware that I could achieve the desired result with a delegate however I am curious about solving this issue with a callback function as I believe this may be an interesting pattern. I began by examining a common pattern for category extension of the UIAlertView class. Code below. Thanks in advance for any suggestions.
import <UIKit/UIKit.h>
#interface UIAlertView (Block)
- (id)initWithTitle:(NSString *)title message:(NSString *)message completion:(void (^)(BOOL cancelled, NSInteger buttonIndex, UITextField *textField))completion cancelButtonTitle:(NSString *)cancelButtonTitle otherButtonTitles:(NSString *)otherButtonTitles, ... NS_REQUIRES_NIL_TERMINATION;
#end
The .m for the category follows:
#import "UIAlertView+Block.h"
#import <objc/runtime.h>
static char const * const alertCompletionBlockTag = "alertCompletionBlock";
#implementation UIAlertView (Block)
- (id)initWithTitle:(NSString *)title
message:(NSString *)message
completion:(void (^)(BOOL cancelled, NSInteger buttonIndex))completion
cancelButtonTitle:(NSString *)cancelButtonTitle
otherButtonTitles:(NSString *)otherButtonTitles, ... {
self = [self initWithTitle:title message:message delegate:self cancelButtonTitle:cancelButtonTitle otherButtonTitles:nil ];
if (self) {
objc_setAssociatedObject(self, alertCompletionBlockTag, completion, OBJC_ASSOCIATION_COPY);
va_list _arguments;
va_start(_arguments, otherButtonTitles);
for (NSString *key = otherButtonTitles; key != nil; key = (__bridge NSString *)va_arg(_arguments, void *)) {
[self addButtonWithTitle:key];
}
va_end(_arguments);
}
[self setAlertViewStyle:UIAlertViewStylePlainTextInput];
return self;
}
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
id completion = objc_getAssociatedObject(self, alertCompletionBlockTag);
[self complete:completion index:buttonIndex];
}
- (void) complete:(void (^)(BOOL cancelled, NSInteger buttonIndex))block index:(NSInteger)buttonIndex {
BOOL _cancelled = (buttonIndex == self.cancelButtonIndex);
block(_cancelled, buttonIndex );
objc_setAssociatedObject(self, alertCompletionBlockTag, nil, OBJC_ASSOCIATION_COPY);
//objc_removeAssociatedObjects(block);
}
#end
Usage for the category is set below. The main problem is my inability to reference the UIAlertView textField at Index 0 from within the completion block.
[[[UIAlertView alloc] initWithTitle:#"Add"
message:#"Add New Asset Type"
completion:^(BOOL cancelled, NSInteger buttonIndex){
if (!cancelled) {
//call on completion of UISheetAction ???
NSLog(#"%#",needToAccessUIAlertView._textFields[0]);
}
}
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"OK", nil] show];
So basically you want to access the alert view from the block. You can do something like this:
__block __weak UIAlertView *alertViewWeak;
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Add"
message:#"Add New Asset Type"
completion:^(BOOL cancelled, NSInteger buttonIndex){
if (!cancelled) {
//call on completion of UISheetAction ???
NSLog(#"%#",[alertViewWeak textFieldAtIndex:0]);
}
}
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"OK", nil];
alertViewWeak = alertView;
[alertView show];
If you want to make a category by yourself, above is good enough.
But, there are many classes that uses delegation pattern. Do you want to make categories one by one?
There is REKit. With it, you can use that classes as if they were Block-based:
UIAlertView *alertView;
alertView = [[UIAlertView alloc]
initWithTitle:#"title"
message:#"message"
delegate:nil
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"OK", nil
];
[alertView
respondsToSelector:#selector(alertView:didDismissWithButtonIndex:)
withKey:nil
usingBlock:^(id receiver, UIAlertView *alertView, NSInteger buttonIndex) {
// Do something…
}
];
alertView.delegate = alertView;
Try this library Here is another useful library to do the same. http://ichathan.com/2014/08/19/ichalertview/
I've created an app which uses the following:
SLComposeViewController isAvailableForServiceType:SLServiceTypeFacebook
SLComposeViewController isAvailableForServiceType:SLServiceTypeTwitter
The code seems to work OK and makes the posts to Facebook and Twitter but then once the posts have been completed and I return back to the app view none of the controls are active and I have to close the app and relaunch for them to work again.
I think I have nested to code incorrectly in the IF statement (posted below), so was wondering if anybody could offer any advice.
I'm very new to Xcode etc so please be patient and kind to me :-)
Thanks in advance
Pete
- (IBAction)postButton:(id)sender
{
if ([SLComposeViewController isAvailableForServiceType:SLServiceTypeFacebook])
{
SLComposeViewController *facebook = [[SLComposeViewController alloc] init];
([SLComposeViewController isAvailableForServiceType:SLServiceTypeTwitter]);
{
SLComposeViewController *twitter = [[SLComposeViewController alloc] init];
facebook = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeFacebook];
[facebook setInitialText:[[self statusMessage]text]];
twitter = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeTwitter];
[twitter setInitialText:[[self statusMessage]text]];
[self presentViewController:facebook animated:YES completion:nil];
[facebook setCompletionHandler:^(SLComposeViewControllerResult result)
{
NSString *output;
switch (result)
{
case SLComposeViewControllerResultCancelled:
output = #"Action Cancelled";
break;
case SLComposeViewControllerResultDone:
output = #"Post Sucessfull";
default:
break;
}
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Facebook" message:output delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alert show];
[self presentViewController:twitter animated:YES completion:nil];
[twitter setCompletionHandler:^(SLComposeViewControllerResult result)
{
NSString *output;
switch (result)
{
case SLComposeViewControllerResultCancelled:
output = #"Action Cancelled";
break;
case SLComposeViewControllerResultDone:
output = #"Tweet Sucessfull";
default:
break;
}
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Twitter" message:output delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alert show];
}];
}
];}
}
}