Mysterious Loading Time of UITableView - ios

For some reason, my app seems to take forever when I try to perform a segue. The mysterious loading time only appears, after I request permission for the AddressBook - if the Permission is already granted, there is no issue at all.
So, before actually using the Segue, i'm doing something like this:
Requesting Permission for Address Book
-(void)requestPermissionForContacts
{
ABAddressBookRef addressbook = ABAddressBookCreateWithOptions(nil, nil);
if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusNotDetermined)
{
ABAddressBookRequestAccessWithCompletion(addressbook, ^(bool granted, CFErrorRef error) {
if (granted)
{
NSLog(#"granted");
[self didGrantPermissions];
} else{
NSLog(#"denied");
[self didNotGrantPermission];
}
});
}
else if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized)
{
[self didGrantPermissions];
} else {
[self didNotGrantPermission];
}
CFRelease(addressbook);
}
Whatever they user clicks:
-(void)didGrantPermissions
{
[self performSegueWithIdentifier:#"addressBook" sender:nil];
}
-(void)didNotGrantPermission
{
[[[UIAlertView alloc] initWithTitle:#"Address Book" message:#"To access this feature, please allow us to access your Address Book" delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil, nil] show];
}
And after that,
prepareForSegue
if ([segue.identifier isEqualToString:#"addressBook"])
{
CWAddressBookViewController *abvc = segue.destinationViewController;
abvc.delegate = self;
abvc.addressType = self.addressType;
}
If it is the first time, the App is accessing this view (so, if its still asking for permission) it takes quite a while (20sec!) until -(void)viewDidLoad is being called on CWAddressBookViewControlller
If the permission is already granted, it is performing the segue as expected in the blink of an eye.
Any idea what im doing wrong here?

Related

iOS camera authorization - wrong status

I use the following code to check and request authorization for the Camera. Problem is the following. The following scenario leads to a wrong authorization status:
User declines authorization for the first time
Terminates the app
Restarts the app
Leaves the application, grants authorization for the camera in settings app
Returns to the app
[AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo] will return AVAuthorizationStatusDeclined (authorized as said).
After terminating and restarting results in AVAuthorizationStatusAuthorized as it should. In this case the user leaves to settings and denies camera access the result will remain AVAuthorizationStatusAuthorized until next restart.
Any ideas what I miss here?
- (void) popCamera {
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
picker.delegate = self;
//picker.allowsEditing = YES;
#if !(TARGET_IPHONE_SIMULATOR)
picker.sourceType = UIImagePickerControllerSourceTypeCamera;
picker.cameraDevice = UIImagePickerControllerCameraDeviceFront;
#endif
self.view.translatesAutoresizingMaskIntoConstraints = YES;
[self presentViewController:picker animated:YES completion:NULL];
}
- (void)camDenied
{
NSLog(#"%#", #"Denied camera access");
NSString *alertText;
NSString *alertButton;
BOOL canOpenSettings = (&UIApplicationOpenSettingsURLString != NULL);
if (canOpenSettings)
{
alertText = LSS(#"DeniedCamera1");
SDCAlertView *alert = [[SDCAlertView alloc]
initWithTitle:LSS(#"DeniedCameraTitle")
message:alertText
delegate:self
cancelButtonTitle:LSS(#"Cancel")
otherButtonTitles:LSS(#"Goto"), nil];
alert.tag = 3491832;
[alert show];
}
else
{
alertText = LSS(#"DeniedCamera2");
SDCAlertView *alert = [[SDCAlertView alloc]
initWithTitle:LSS(#"DeniedCameraTitle")
message:alertText
delegate:self
cancelButtonTitle:LSS(#"Cancel")
otherButtonTitles:nil];
alert.tag = 3491832;
[alert show];
}
}
- (IBAction) onTakePhoto:(id)sender {
AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
if(authStatus == AVAuthorizationStatusAuthorized)
{
[self popCamera];
}
else if(authStatus == AVAuthorizationStatusNotDetermined)
{
NSLog(#"%#", #"Camera access not determined. Ask for permission.");
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted)
{
if(granted)
{
[self popCamera];
}
else
{
[self camDenied];
}
}];
}
else if (authStatus == AVAuthorizationStatusRestricted)
{
SDCAlertView *alert = [[SDCAlertView alloc]
initWithTitle:LSS(#"RestrictCameraTitle")
message:LSS(#"RestrictCamera")
delegate:self
cancelButtonTitle:LSS(#"OK")
otherButtonTitles:nil];
}
else
{
[self camDenied];
}
}
Credits for the original code: Is there a way to ask user for Camera access after they have already denied it on iOS 8?
This seems to be the intended behaviour. If Apple would want you to react to authorization changes at runtime, there would be a notification that tells you that it changed.
But right now, there is no such notification (as far as I can see). You just call +authorizationStatusForMediaType: and it either returns a definitive status (like denied or authorized), or it returns AVAuthorizationStatusNotDetermined to tell you that you need to request authorization via requestAccessForMediaType:completionHandler:.
Unfortunately, this isn't an authoritative answer; I'm just drawing conclusions and guessing here. You might want to ask on Apple's developer forum and hope to get an answer from an Apple engineer.

How can I get the contact ID

I have a problem with getting the contact ID from the address book.
My .m file:
-(IBAction)test:(id)sender
{
ABPeoplePickerNavigationController* picker = [[ABPeoplePickerNavigationController alloc] init];
picker.peoplePickerDelegate = self;
[self presentViewController:picker animated:YES completion:nil];
}
-(void)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker didSelectPerson:(ABRecordRef)person
{
[peoplePicker dismissViewControllerAnimated:YES completion:nil];
ABRecordID recordID = ABRecordGetRecordID(person);
label.text = [NSString stringWithFormat:#"%d", recordID];
}
-(void)peoplePickerNavigationControllerDidCancel:(ABPeoplePickerNavigationController *)peoplePicker
{
[peoplePicker dismissViewControllerAnimated:YES completion:nil];
}
I imported the AddressBook and the AddressBookUI into the .h file.
It works all but I get the wrong ID.
Everytime I only get the ID -1 and that stands for error.
Hope you can help me.
Thanks a lot,
Christian
In iOS 8, it no longer automatically requests permission for the address book when just showing the people picker. (The idea is that is that the picker, alone, is not really giving the app access to your contacts, so no permission should be required. It offers low-friction UI when app isn't really getting the contact details.)
But, in your case, you really are trying to programmatically retrieve information about the contact. For that, you do need permission. So, prior to presenting the picker, you might want to ask permission to the address book:
ABAuthorizationStatus status = ABAddressBookGetAuthorizationStatus();
if ((status == kABAuthorizationStatusDenied || status == kABAuthorizationStatusRestricted)) {
NSLog(#"User previously denied permission; show error message that they must go to settings and give this app permission");
return;
} else if (status == kABAuthorizationStatusNotDetermined) {
CFErrorRef error;
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, &error);
if (!addressBook) {
NSLog(#"unable to open address book: %#", CFBridgingRelease(error));
return;
}
ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error) {
if (granted) {
NSLog(#"OK");
} else {
NSLog(#"Not ok");
}
CFRelease(addressBook);
});
}
Here my solution how to get the ID of a contact:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
ABAuthorizationStatus status = ABAddressBookGetAuthorizationStatus();
if ((status == kABAuthorizationStatusDenied || status == kABAuthorizationStatusRestricted)) {
NSLog(#"User previously denied permission; show error message that they must go to settings and give this app permission");
return;
} else if (status == kABAuthorizationStatusNotDetermined) {
CFErrorRef error;
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, &error);
if (!addressBook) {
NSLog(#"unable to open address book: %#", CFBridgingRelease(error));
return;
}
ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error) {
if (granted) {
NSLog(#"OK");
} else {
NSLog(#"Not ok");
}
CFRelease(addressBook);
});
}
ABPeoplePickerNavigationController* picker = [[ABPeoplePickerNavigationController alloc] init];
picker.peoplePickerDelegate = self;
[self presentViewController:picker animated:YES completion:nil];
}
-(void)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker didSelectPerson:(ABRecordRef)person
{
[peoplePicker dismissViewControllerAnimated:YES completion:nil];
ABRecordID recordID = ABRecordGetRecordID(person);
NSLog([NSString stringWithFormat:#"%#"], recordID);
}
Thanks to Rob who helped me out :)

UIAlertView and Requesting Access to Addressbook

if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusNotDetermined) {
ABAddressBookRequestAccessWithCompletion(addressBookRef, ^(bool granted, CFErrorRef error) {
if (granted) {
// First time access has been granted, add the contact
[self prepareContactsIDs];
} else {
UIAlertView *accessDenied = [[UIAlertView alloc] initWithTitle:#"Need Access to Addressbook" message:#"KeepItClean requires an access to addressbook in order to be usable" delegate:self cancelButtonTitle:#"Ok" otherButtonTitles:nil];
[accessDenied show];
}
});
}
else if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized) {
// The user has previously given access, add the contact
if ([self isFirstRun]) {
[self prepareContactsIDs];
}
}
else {
// The user has previously denied access
// Send an alert telling user to change privacy setting in settings app
UIAlertView *accessDenied = [[UIAlertView alloc] initWithTitle:#"Need Access to Addressbook" message:#"KeepItClean requires an access to addressbook in order to be usable" delegate:self cancelButtonTitle:#"Ok" otherButtonTitles:nil];
[accessDenied show];
}
There is something i don't understand here, when the user is prompted to give access, if he taps cancel, i don't get my 2nd UIAlertView showing up, the (accessDenied) alert view.
Also i feel there is something i don't understand that is related to dispatching and queues.
Try this
ABPeoplePickerNavigationController *peoplePicker = [[ABPeoplePickerNavigationController alloc] init];
peoplePicker.peoplePickerDelegate= self;
ABAddressBookRef UsersAddressBook = ABAddressBookCreateWithOptions(NULL, NULL);
if (ABAddressBookGetAuthorizationStatus()!= kABAuthorizationStatusDenied)
{
//Show alert of access
}
else
{
//Show alert of access denied
}

Wait for user to respond to request for addressbook access in iOS?

I would like my app to only ask the user for permission to access the Contacts addressbook until the user access the proper function in my app. I really don't want to request permission when the app loads.
As a result I've used the following code:
- (IBAction)importClientsButtonPressed:(id)sender {
// request access to Contacts address book
CFErrorRef addyError = NULL;
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, &addyError);
if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusNotDetermined) {
ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef addyError) {
});
}
if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized) {
_importContactsActionSheet = [[UIActionSheet alloc] initWithTitle:#"Import Client from Contacts"
delegate:self
cancelButtonTitle:#"Cancel"
destructiveButtonTitle:nil
otherButtonTitles:#"Primary Contact", #"Secondary Contact", nil];
_importContactsActionSheet.actionSheetStyle = UIActionSheetStyleBlackTranslucent;
[_importContactsActionSheet showFromRect:self.importClientsButton.frame inView:self.importClientsButton.superview animated:YES];
} else {
// the user has previously denied access - send alert to user to allow access in Settings app
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Privacy Settings"
message:#"This app does not have access to your contacts. You can enable access in Privacy Settings."
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
}
}
However, the permission dialog does not halt the app and wait for a response...the code following the request continues to run. As a result I get a whack of messages popping up out of order.
Is there any way to have the whole app halt until a response comes back from the permission request dialog?
Thanks!
In your code above also you have this:
ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef addyError) {
if(granted) {
//Put here your code
}
});
so finally i would write the code in this way:
- (IBAction)importClientsButtonPressed:(id)sender {
__weak typeof(self) weakSelf = self;
// request access to Contacts address book
CFErrorRef addyError = NULL;
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, &addyError);
if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusNotDetermined) {
ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef addyError) {
if(granted) {
[weakSelf openImportContact];
}
});
} else if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized) {
[weakSelf openImportContact];
} else {
// the user has previously denied access - send alert to user to allow access in Settings app
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Privacy Settings"
message:#"This app does not have access to your contacts. You can enable access in Privacy Settings."
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
}
}
- (void)openImportContact {
dispatch_async(dispatch_get_main_queue(), ^{
_importContactsActionSheet = [[UIActionSheet alloc] initWithTitle:#"Import Client from Contacts"
delegate:self
cancelButtonTitle:#"Cancel"
destructiveButtonTitle:nil
otherButtonTitles:#"Primary Contact", #"Secondary Contact", nil];
_importContactsActionSheet.actionSheetStyle = UIActionSheetStyleBlackTranslucent;
[_importContactsActionSheet showFromRect:self.importClientsButton.frame inView:self.importClientsButton.superview animated:YES];
});
}

App crashed after enable or disable calendar from settings

I have implemented EKEventStore, EKCalendar in my app to make the EKEvent within the app. It is working fine until or unless I didn't change permissions from the settings. But when I changed the requested access (on/off or off/on) from the settings, the app get crashed. I am unable to find the error. If someone has idea then please help me out. Here is the code I have implemented :
self.eventStore = [[EKEventStore alloc] init];
[self checkEventStoreAccessForCalendar];
-(void)checkEventStoreAccessForCalendar
{
EKAuthorizationStatus status = [EKEventStore authorizationStatusForEntityType:EKEntityTypeEvent];
switch (status)
{
case EKAuthorizationStatusAuthorized: [self accessGrantedForCalendar];
break;
case EKAuthorizationStatusNotDetermined: [self requestCalendarAccess];
break;
case EKAuthorizationStatusDenied:
case EKAuthorizationStatusRestricted:
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Privacy Warning" message:#"Permission was not granted for Calendar"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
}
break;
default:
break;
} }
-(void)requestCalendarAccess
{
[self.eventStore requestAccessToEntityType:EKEntityTypeEvent completion:^(BOOL granted,
NSError *error)
{
if (granted)
{
dispatch_async(dispatch_get_main_queue(), ^{
});
}
}];
}
-(void)accessGrantedForCalendar
{
self.defaultCalendar = self.eventStore.defaultCalendarForNewEvents;
EKEvent *addEvent = [EKEvent eventWithEventStore:self.eventStore];
addEvent.title = [NSString stringWithFormat:#"%#", textcontainer.text];
}
#pragma mark EKEventEditViewDelegate
-(void)eventEditViewController:(EKEventEditViewController *)controller
didCompleteWithAction:(EKEventEditViewAction)action{
[self dismissViewControllerAnimated:YES completion:^
{
if (action != EKEventEditViewActionCanceled)
{
dispatch_async(dispatch_get_main_queue(), ^{
});
}
}];
}
- (EKCalendar *)eventEditViewControllerDefaultCalendarForNewEvents:(EKEventEditViewController *)controller
{
return self.defaultCalendar;
}
When your app goes to the background it should save all its states and should be prepared to get killed. When someone switches off access to calendar, the easiest way for the system to deals with that is to kill your app, and that is why its happening. If you switch back to your app by clicking 'Back to Your-App' on top, you can notice that your app will be launching from the beginning, not from where you left. Try not using the breakpoint and you will notice the change.
So its not a crash actually.

Resources