Why when using GCD, UIAlertController display is delayed or not shown? - objective-c

I have this code, which gets called from another class like this:
// send message not to turn off machine and display network indicator
[cm displayAlert: #"Warning" andData: #"Database restore is about to begin; DO NOT turn iPad OFF during restore!" andTag:0 andViewController:self];
and this is the code:
#pragma mark - displayAlert
- (void) displayAlert: (NSString *)alertTitle andData: (NSString *) alertMessage andTag: (int) tag andViewController: vc {
UIAlertController *alertController = [UIAlertController
alertControllerWithTitle:alertTitle
message:alertMessage
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *cancelAction = [UIAlertAction
actionWithTitle:NSLocalizedString(#"Cancel", #"Cancel action")
style:UIAlertActionStyleCancel
handler:^(UIAlertAction *action)
{
NSLog(#"Cancel action");
}];
UIAlertAction *okAction = [UIAlertAction
actionWithTitle:NSLocalizedString(#"OK", #"OK action")
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action)
{
NSLog(#"OK action");
}];
[alertController addAction:cancelAction];
[alertController addAction:okAction];
dispatch_async(dispatch_get_main_queue(), ^{
[vc presentViewController:alertController animated:YES completion:nil];
});
}
The problem is that the presentation is either delayed or not shown at all. I used the GCD code which many, if not all, of the examples on SO show... what could be causing this delay?

Related

UiAlertController disappears automatically, without user input

I am attempting to present an warning UIAlertAction when a user has not enter text into a particular field. When the field is missing text and the user presses send, the alert appears for a moment then dismisses.
#pragma mark - Actions
- (IBAction)sendPressed:(id)sender
{
if (_titleLabel.text.length == 0)
{
self.alert = [UIAlertController alertControllerWithTitle:#"Sorry"
message:#"Please enter a title, it is required"
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *defaultAction = [UIAlertAction actionWithTitle:#"OK" style:UIAlertActionStyleCancel
handler:^(UIAlertAction *action) {
[self dismissViewControllerAnimated:YES completion:nil];
}];
[self.alert addAction:defaultAction];
[self presentViewController:self.alert animated:YES completion:nil];
}
else
{
}
if (_delegate && [_delegate respondsToSelector:#selector(reportControllerDidPressSend:)]) {
[_delegate reportControllerDidPressSend:self];
}
Are you aware, that you have a empty else branch and an independent second if statement? I am quite sure, you want an else if-state meant instead
Also the test if delegate is nil isn't needed.
#pragma mark - Actions
- (IBAction)sendPressed:(id)sender
{
if (_titleLabel.text.length == 0) {
self.alert = [UIAlertController alertControllerWithTitle:#"Sorry"
message:#"Please enter a title, it is required"
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *defaultAction = [UIAlertAction actionWithTitle:#"OK" style:UIAlertActionStyleCancel
handler:^(UIAlertAction *action) {
[self dismissViewControllerAnimated:YES completion:nil];
}];
[self.alert addAction:defaultAction];
[self presentViewController:self.alert animated:YES completion:nil];
} else if([_delegate respondsToSelector:#selector(reportControllerDidPressSend:)])) {
[_delegate reportControllerDidPressSend:self];
}
}

iPad crashes when showing photos or taking photo--nil source view

The follow code crashes when run on an iPad with the error message:
(<UIPopoverPresentationController: 0x1377a19e0>) should have a non-nil
sourceView or barButtonItem set before the presentation occurs.'
However, in my code I have the following, and I checked with breakpoints and console prinouts to ensure sourceview and barbutton are not nil. What do I need to do to avoid this? The error only appears on an iPad. Here is the method that causes trouble:
- (void)showChooseImageOptions {
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet];
UIAlertAction *chooseAction = [UIAlertAction actionWithTitle:#"Choose From Photos" style:UIAlertActionStyleDestructive handler:^(UIAlertAction *action) {
[self showLibrary];
}];
UIAlertAction *takeAction = [UIAlertAction actionWithTitle:#"Take Photo" style:UIAlertActionStyleDestructive handler:^(UIAlertAction *action) {
[self showCamera];
}];
self.directionsTextView.text=#"";
[alertController addAction:chooseAction];
[alertController addAction:takeAction];
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:#"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
if (self.imageView.image) {
self.directionsTextView.text=#"Tap the circle to change the image";
} else{
self.directionsTextView.text=#"Tap the blue circle to choose or take an image";
}
}];
[alertController addAction:cancelAction];
alertController.view.tintColor=[ColorSuperclass returnApplicationMainColor];
alertController.popoverPresentationController.barButtonItem = self.navigationItem.rightBarButtonItem;
if ( [alertController respondsToSelector:#selector(popoverPresentationController)] ) {
// at least iOS8
alertController.popoverPresentationController.sourceView = self.view;
}
[self presentViewController:alertController animated:YES completion:nil];
}
And the method showCamera and showLibrary are nothing but:
-(void)showCamera {
if (TARGET_IPHONE_SIMULATOR) {
//do nothing, the simulator cannot handle pressing the take photos...
} else{
[self showImagePickerForSourceType:UIImagePickerControllerSourceTypeCamera];
}
}
- (void)showLibrary {
[self showImagePickerForSourceType:UIImagePickerControllerSourceTypePhotoLibrary];
}
and the show source type method:
- (void)showImagePickerForSourceType:(UIImagePickerControllerSourceType)sourceType {
UIImagePickerController *imagePickerController = [[UIImagePickerController alloc] init];
imagePickerController.modalPresentationStyle = UIModalPresentationPopover; //this line is very important, because otherwise, the tab bar could go out of scope (a consequence of using modal segues and tab controllers!)
imagePickerController.sourceType = sourceType;
imagePickerController.delegate = self;
imagePickerController.allowsEditing = YES;
[self presentViewController:imagePickerController animated:YES completion:nil];
}

UIAlertController not allowing click

I am generating a UIAlertController for use in iOS8 to replace a UIActionSheet function.
The controller presents correctly, but I cannot click anywhere in the controller to get a response. I put in simple NSLog into the handler, and it is absolutely not firing.
Can anyone shed some light on this problem?
BTW, in iOS8 the UIActionSheet is ALSO not generating click events, but in iOS7 this works fine with absolutely no code change.
here is the code:
NSString *alertMessage = #"How do you wish to share ?";
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:#"Share Image"
message:alertMessage
preferredStyle:UIAlertControllerStyleAlert];
// You can add as many actions as you want
UIAlertAction *emailShare = [UIAlertAction actionWithTitle:#"Share using email"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {
NSLog(#"EMAIL");
}];
UIAlertAction *smShare = [UIAlertAction actionWithTitle:#"Share using twitter/facebook"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {
[self actionSheet:nil didDismissWithButtonIndex:1];
}];
UIAlertAction *cancelShare = [UIAlertAction actionWithTitle:#"cancel"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {
[self actionSheet:nil didDismissWithButtonIndex:2];
}];
// Add actions to the controller so they will appear
[alertController addAction:emailShare];
[alertController addAction:smShare];
[alertController addAction:cancelShare];
// Finally present the action
[self presentViewController:alertController animated:YES completion:nil];
You have a few different issues with your code. I've commented on the changes that are necessary. Let me know if you have any questions.
// No need to create a NSString. Just set the message.
// NSString *alertMessage = #"How do you wish to share?";
// If you'd like a UIActionSheet use UIAlertControllerStyleActionSheet as the preferredStyle
// Opposed to UIAlertControllerStyleAlert, which presents a UIAlertView
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:#"Share Image"
message:#"How do you wish to share?" // Message Set
preferredStyle:UIAlertControllerStyleActionSheet]; // Changed to show action sheet
UIAlertAction *emailShare = [UIAlertAction actionWithTitle:#"Share using email"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {
[alertController dismissViewControllerAnimated:YES completion:nil];
// Call your function here
// ie. [self emailShare];
NSLog(#"First Button");
}];
UIAlertAction *smShare = [UIAlertAction actionWithTitle:#"Share using twitter/facebook"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {
// [self actionSheet:nil didDismissWithButtonIndex:1];
// Where did you create actionSheet?
// didDismissWithButtonIndex: is not neccessary
// Just simply dismiss the UIAlertController
[alertController dismissViewControllerAnimated:YES completion:nil];
// And then call your function here
// ie. [self twitterShare];
NSLog(#"Second Button");
}];
UIAlertAction *cancelShare = [UIAlertAction actionWithTitle:#"Cancel"
style:UIAlertActionStyleCancel // Set style to cancel
handler:^(UIAlertAction *action) {
// [self actionSheet:nil didDismissWithButtonIndex:2];
// Where did you create actionSheet?
// didDismissWithButtonIndex: is not neccessary
// Just simply dismiss the UIAlertController
[alertController dismissViewControllerAnimated:YES completion:nil];
NSLog(#"Cancel Button");
}];
[alertController addAction:emailShare];
[alertController addAction:smShare];
[alertController addAction:cancelShare];
[self presentViewController:alertController animated:YES completion:nil];

How to get UIAlertAction (UIAlertController) response from block handler?

I have this code where I want to display a message with a YES or NO response using UIAlertAction; this is my code:
-(void) displayAlert: (NSString *)alertTitle andMessage: (NSString *)alertMessage andViewController: (UIViewController *) viewController andTag:(int)tag {
// create controller
UIAlertController *alertController = [UIAlertController
alertControllerWithTitle:alertTitle
message:alertMessage
preferredStyle:UIAlertControllerStyleAlert];
// add buttons
if(tag == 0) { // ok/cancel response
UIAlertAction *cancelAction = [UIAlertAction
actionWithTitle:NSLocalizedString(#"Cancel", #"Cancel action")
style:UIAlertActionStyleCancel
handler:^(UIAlertAction *action)
{
}];
UIAlertAction *okAction = [UIAlertAction
actionWithTitle:NSLocalizedString(#"OK", #"OK action")
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action)
{
}];
[alertController addAction:cancelAction];
[alertController addAction:okAction];
}
else if(tag == 1) { // yes/no response
UIAlertAction *noAction = [UIAlertAction
actionWithTitle:NSLocalizedString(#"No", #"No action")
style:UIAlertActionStyleCancel
handler:^(UIAlertAction *action)
{
}];
UIAlertAction *yesAction = [UIAlertAction
actionWithTitle:NSLocalizedString(#"Yes", #"Yes action")
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action)
{
}];
[alertController addAction:noAction];
[alertController addAction:yesAction];
}
// display controller
[viewController presentViewController: alertController animated:YES completion:nil];
}
My problem is that the block does not allow a return (according to docs). So how do I return the response to the calling method? (it's not inline, it's in a class of common methods because it's used often).
You can't have a return value from block. Instead you can do the following:
Add completion block parameter to your displayAlert method. And include into this block definition an argument that would indicate button pressed (yes/no or whatever)
Call this completion block from both block handlers that you already have and pass proper value into it.
This way your application code can define one completion block that will be executed in both cases (when user tapped Yes or No) and you can handle it there.

presentViewController not working in viewDidLoad

In this code the alert action is shown every time the app become active:
- (void)viewDidLoad {
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationDidBecomeActive:)
name:UIApplicationDidBecomeActiveNotification
object:nil];
}
- (void)applicationDidBecomeActive:(NSNotification *)notification
{
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:#"Please make your choice"
message:#"Would you like a cup of coffee?"
preferredStyle:UIAlertControllerStyleActionSheet];
UIAlertAction *yesAction = [UIAlertAction actionWithTitle:#"YES"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {
NSLog(#"You tapped YES");
}];
UIAlertAction *maybeAction = [UIAlertAction actionWithTitle:#"MAYBE"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {
NSLog(#"You tapped MAYBE");
}];
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:#"Cancel"
style:UIAlertActionStyleCancel
handler:nil];
[alertController addAction:yesAction];
[alertController addAction:maybeAction];
[alertController addAction:cancelAction];
[self presentViewController:alertController animated:YES completion:nil];
}
Also, everything works as expected if I move the code block of the UIAlertController in viewDidAppear method.
But if I move the UIAlertController in viewDidLoad :
- (void)viewDidLoad {
UIAlertController *alertController [...]
[...]
[self presentViewController:alertController animated:YES completion:nil];
}
it doesn't works. The alert is not shown.
In viewDidLoad it is not part of the view hierarchy at that time hence it is silently ignored, on viewWillAppear at this time the view hierarchy is already set up hence the reason it works.