Why UIAlertView prevents my modal view controller from being displayed? - objective-c

Let's say that when the user presses a button a message needs to be shown and then a modal view controller is displayed.
I would write something like that :
- (void)pickImageButtonPressed:(id)button {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Alert"
message:#"Some alert"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
[alert release];
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
picker.delegate = self;
picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
[[UIApplication sharedApplication].keyWindow.rootViewController
presentModalViewController:picker animated:YES];
[picker release];
}
With this code, the modal view controller is simply not displayed !
However, if I switch the two blocks of code (show modal first, then show the alert), the view controller is displayed modally and the alert shows up above it, exactly as I want.
My question is: why the alert prevents the modal view controller from being displayed ?
Please note that I do not want to wait for alert to be dismissed before calling presentModalViewController.
Update :
I figured out that my production code was a bit more complicated than what I first put. I updated the example code and then finally found a simple example to reproduce the problem.
Note that in my production code, the popup is displayed from a place where I don't have any reference to a view controller, that's why I use [UIApplication sharedApplication].keyWindow.rootViewController instead of a direct reference to a view controller

I finally found the answer. The issue really was on this line:
[[UIApplication sharedApplication].keyWindow.rootViewController
presentModalViewController:picker animated:YES];
When an alert is displayed, [UIApplication sharedApplication].keyWindow.rootViewController is nil !. That's why the picker was never displayed. And that's why it worked when I switched the two blocks.
Now I need to find a way to get the most relevant view controller to present the picker modally...

Related

is it possible to show the alert message on top of another sharing dialog

Im using this code to try and show a alert message on the iphone
[[UIApplication sharedApplication].delegate.window.rootViewController presentViewController:alert animated:YES completion:nil];
it works fine.
But is it possible to show the alert message on top of another sharing dialog also shown using presentViewController:
at the moment it shows a warning in the console and the alert does not show, only shows the popup for the sharing activity?
I can get this to work using UIAlertView but that deprecated now
For future reference, it would be good to include the text of the warning you're receiving in the question as well.
My guess is it's along the lines of:
Warning: Attempt to present <UIAlertController: 0x7fe676036000> on <ViewController: 0x7fe674e02f50> which is already presenting <UIActivityViewController: 0x7fe67580f800>
Which basically indicates you can't present another view controller using a view controller that's already presenting a separate view controller.
One way you could work around this is to climb the presentedViewControllers to locate a view controller which isn't already presenting and then present from that (in your case you should only need to move up one presentedViewController to your UIActivityViewController).
Here's an example using the UIWindowScene to grab the rootViewController (iOS 13+), but should basically be the same idea for using the appDelegate's window as well:
- (IBAction)showShareSheetAndAlert:(id)sender {
UIImage *homerImage = [UIImage imageNamed:#"Homer"];
UIActivityViewController *shareSheet = [[UIActivityViewController alloc] initWithActivityItems:#[homerImage] applicationActivities:nil];
[self presentViewController:shareSheet animated:YES completion:^{
UIAlertController *alert = [UIAlertController alertControllerWithTitle:#"Test Alert" message:#"Here's a test alert on top of a share sheet" preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *dismissAction = [UIAlertAction actionWithTitle:#"Dismiss" style:UIAlertActionStyleDefault handler:nil];
[alert addAction:dismissAction];
UIWindowScene *windowScene = (UIWindowScene *)[[[[UIApplication sharedApplication] connectedScenes] allObjects] firstObject];
UIViewController *viewControllerToPresentOn = windowScene.windows.firstObject.rootViewController;
// keep climbing the presented view controllers until we find the top one which isn't presenting
while (viewControllerToPresentOn.presentedViewController != nil) {
viewControllerToPresentOn = viewControllerToPresentOn.presentedViewController;
}
[viewControllerToPresentOn presentViewController:alert animated:YES completion:nil];
}];
}
Which will result in this:

Stop NSPopover from closing if NSAlert dialog appears?

Hi I have a NSStatusItem that shows a popover when clicked. One option in the popover is to delete something; at this point I ask the user if they are sure.
The problem is that the Popover automatically closes as soon as the Alert appears.
I have tried all the different behavior settings like NSPopoverBehaviorSemitransient, NSPopoverBehaviorTransient and NSPopoverBehaviorApplicationDefined to no avail.
I also try catching it in - (BOOL)popoverShouldClose:(NSPopover *)popover but this isn't called when NSAlert appears.
Nothing seems to stop an NSAlert from closing a popover.
NSAlert *alert = [[NSAlert alloc] init];
[alert addButtonWithTitle:#"OK"];
[alert addButtonWithTitle:#"Cancel"];
[alert setMessageText:msg];
[alert setInformativeText:#"Warning, delete this folder?"];
[alert setAlertStyle:NSCriticalAlertStyle];
// Show the alert
if ([alert runModal] == NSAlertFirstButtonReturn) {
I also tried showing the alert as a sheet in the popover which kind of works but then the popover stays around no matter if I click outside it.

ModalViewController Without Dimming / Disabling current viewcontroller

I am displaying aNavController as a modalViewController in a specific frame CGRectMake(40,50, 400, 500). Which is working fine. Now I have a button in self (viewcontroller on which modalViewController is presented), on pressing that button I need to display some message on aNavController. But problem is when I am presenting a modalViewController. Whole screen area got dimmed/disabled. So, not able to touch/click that button in self.
Here is my code to present a view controller. I thought, I am missing something here. Please Help. Thanks in advance.
aNavController.modalPresentationStyle = UIModalPresentationFormSheet;
anavController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentModalViewController:aNavController animated:YES];
aNavController.view.superview.frame = CGRectMake(40,50, 400, 500);
presentModalViewController create a modal dialog. When modal view controller is up, users can't do any thing on parent view until the the modal view is dismissed.
The problem is you're instantiating a UIAlertView at the same time as the presentModalViewController call your modal view in UIAlertView's delegate method clickedButtonAtIndex.
Like so:
- (IBAction)clickedMyButton:(id)sender
{
UIAlertView *alertView = [[UIAlertView alloc]
initWithTitle: #"Title"
message: #"Message"
delegate:self
cancelButtonTitle:#"Close Button"
otherButtonTitles:#"Modal Button", #"Some Other Button", nil];
[alertView show];
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
if (buttonIndex == 0) {
NSLog(#"User Selected Cancel");
}
else if (buttonIndex == 1) {
NSLog(#"Modal Button Clicked");
aNavController.modalPresentationStyle = UIModalPresentationFormSheet;
anavController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentModalViewController:aNavController animated:YES];
aNavController.view.superview.frame = CGRectMake(40,50, 400, 500);
}else {
NSLog(#"Some Other Button Clicked");
}
}
Or, if you wish for your UIAlertView to show on top of your navigation controller, ignore the above and simply wait to call your alert until the navigation controllers - (void)viewDidAppear:(BOOL)animated method.
In addition, I recommend you change your frame to stay within the bounds of the screen unless absolutely necessary. ex: CGRectMake(40, 50, 320, 480);
Finally, I am able to do workaround the things which work same way as a UIModalPresentationFormSheet.
I added the aNavController as a subview to the [[UIApplication sharedApplication] keyWindow] and which solves my all the problems.
Thank you all for your comments.

How can I show UIActionSheet to confirm moving back in Navigation View

Using Xcode I have View A that navigates to View B.
Upon pressing the Back UIBarButtonItem, I'm trying present the user with a UIActionSheet to confirm navigation to move back to View A.
What do I need to do in code to stop the view from navigating back and then (depending on user input) move back or stay on the current screen?
add a backbutton programmatically.
eg.
UIButton *backBtn= [[UIButton alloc] initWithFrame:CGRectMake(0,0,54,30)];
[backBtn addTarget:self action:#selector(backButtonPressed:)forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *backBarButton = [[UIBarButtonItem alloc] initWithCustomView:backBtn];
[backBtn release];
[[self navigationItem] setLeftBarButtonItem:backBarButton];
[backBarButton release];
//backButtonPressed is the selector for backBtn
Then present you ActionSheet from that selector and based on user either navigate to previous viewController or dont.
To navigate to previous page, use popViewMethod.
`
You should not present UIActionSheet for every other action.It would be better to use UIAlertView for this purpose. According to Apple UIActionsheet Guidelines :-
Provide alternate ways a task can be completed. An action sheet allows you to provide a range of choices that make sense in the context of the current task, without giving these choices a permanent place in the user interface.
Get confirmation before completing a potentially dangerous task. An action sheet prompts users to think about the potentially dangerous effects of the step they’re about to take and gives them some alternatives. This type of communication is particularly important on iOS-based devices because sometimes users tap controls without meaning to.
for UIAlertView :-
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Alert View"
message:#"Do You want to go back to previous screen?"
delegate:self
cancelButtonTitle:#"NO"
otherButtonTitles:#"YES",nil];
[alertView show];
[alertView release];
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex == 0) {
NSLog(#"THE 'NO' BUTTON WAS PRESSED");
}
if (buttonIndex == 1) {
NSLog(#"THE 'YES' BUTTON WAS PRESSED");
}
}
Implement this on action of back button of UINavigationController.According to the buttons pressed "YES" or "NO" , you can allow navigation.Also conform to UIAlerrtVIewDelegate protocol.

NavigationController is not popping the Pushed View on Back button

Having a simple Navigation Controller in place (starting the Navigation Based Application project) I created a new View with a XIB file.
on my HomeViewController (Home screen with all options as UIButton's I have:
#implementation HomeViewController
-(IBAction) optionChoosed:(UIButton *)button
{
NSString *msg = [NSString stringWithFormat:#"Button: %d", button.tag];
UIAlertView *alert=[[UIAlertView alloc] initWithTitle:#"Hi" message:msg delegate:nil cancelButtonTitle:#"Go away" otherButtonTitles:nil];
switch (button.tag) {
case 13:
// Simple Search
[self loadSimpleSearch]; break;
default:
[alert show];
break;
}
[alert release];
}
-(void)loadSimpleSearch
{
SimpleSearchViewController *controller =
[[SimpleSearchViewController alloc] initWithNibName:#"SimpleSearchViewController" bundle:nil];
[self.navigationController pushViewController:controller animated:YES];
[controller release];
}
witch works great!
it Pushes the View into the front of the stack!
Now, because in my 2nd view SimpleSearchViewController I have self.title = #"myTitle"; I get the Title in the NavigationBar as well the back button (as I have the same setting on the HomeViewController)
I thought that the NavigationViewController would handle the pop of the current view, but it does not.
What do I have to do, to pop the SimpleSearchViewController?
Where do I use [self.navigationController popViewControllerAnimated:YES];
as the view continues there, and ViewDidUnload is never called.
My idea was that this should be handle in the first ViewController, the HomeViewController but I have no idea what is the method I should hook to, and I read the documentation and I can't figure it out :-/
Any help is greatly appreciated, thank you.
HomeViewController
alt text http://cl.ly/XNS/Screen_shot_2010-04-21_at_22.38.51.png
SimpleSearchViewController
alt text http://cl.ly/YDw/Screen_shot_2010-04-21_at_22.40.00.png
SimpleSearchViewController after pressing the Back button
alt text http://cl.ly/XLO/Screen_shot_2010-04-21_at_22.40.21.png
To add the image from a comment that asks if HomeViewController as the root controller for the NavigationViewController
Not sure if this helps but when implementing navigationItem in code, if you don't call the super the popping functionality will not be present
-(UINavigationItem*) navigationItem
{
UINavigationItem* item = [super navigationItem];
item.title = #"search";
return item;
}