Running NSAlert within NSPopover - objective-c

we are developing a menu bar item app,
and I'd love to write a NSAlert category which shows the alert within a NSPopover, that appears below the NSStatusItem.
So far, the category implements the following new method:
- (void) runAsMenuItemPopUpWithCompletionBlock:(NSAlertCompletionBlock)block {
// Get content view of NSAlert
NSView *alertContentView = [self.window contentView];
// Ask the menu item to show the view as a NSPopOver
[[GFMenuItem sharedInstance] popOverView:alertContentView];
// (...) Handle response with callback
}
But opening an alert
NSAlert *alert = [NSAlert alertWithMessageText:#"Learn more?" defaultButton:#"Learn more" alternateButton:#"Cancel" otherButton:nil informativeTextWithFormat:#"Do you want to view detailed information?"];
[alert runAsMenuItemPopUpWithCompletionBlock:nil];
results in the following visualization:
The problem is the third empty button, the help button and the Checkbox, which have not been set up to be shown. Any idea on how to get rid of them if they have not been set up?

Turns out you can call [alert layout] to trigger the manual layout processing. It will hide any buttons which aren't set up to show!
Corrected method:
- (void) runAsMenuItemPopUpWithCompletionBlock:(NSAlertCompletionBlock)block {
// Trigger the layout processing and get content view of NSAlert
[self layout];
NSView *alertContentView = [self.window contentView];
// Ask the menu item to show the view as a NSPopOver
[[GFMenuItem sharedInstance] popOverView:alertContentView];
// (...) Handle response with callback
}

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:

while UIAlertController is shown, parent's viewDidDisappear is not called (only on IOS8)

sadly i had to migrate from UIActionSheet to UIAlertController because IOS8 removed the first's functionality.
before using that, every time i went to and from home screen, the parent's view "viewDidDisappear" was called so i could dismiss the menu i've created.
now it's not called anymore.
the code looks like:
UIAlertController *alert = [UIAlertController .... preferredStyle:UIAlertControllerSTyleActionSheet]
UIAlertAction* a = [....]
[alert addAction: a];
alert.popoverPresentationController.barButtonItem = self.myButton // the bar button from my view
[self presentViewController:alert animated:YES completion:nil] // maybe im not showing the view well?
i have no idea how to continue from here.. would love some help
Thanks.
== EDIT ==
i saw that it happens also with the deprecated UIActionSheet and also with UIDcoumentInteractionController - ONLY in IOS8, not in IO7
Maybe I'm thinking too simple here, but wouldn't it be the easiest to put whatever code you had in a separate method and call that method from viewDidDisappear and after that bit of code you use to present that alert controller?
- (void)viewDidDisappear {
[self hideMenu];
}
- (void)hideMenu {
// Do all stuff to hide the menu you previously did in viewDidDisappear
}
And alter that presenting code like this
// more code here
[self presentViewController:alert animated:YES completion:nil];
[self hideMenu];

Pressing Action button twice crashes app

My app is crashing in the iPad simulator when I press the action button again once it has already been pressed to open my activity view. I am concerned that this will be an issue if the user wants to press the button again to close the Popover rather than touching outside of it. Any suggestions are appreciated :)
FYI the Action button is a UIToolbar button.
In the .h
#property (strong, nonatomic) UIPopoverController *popup;
#property (strong, nonatomic) UIPopoverController *activityViewProp;
In the .m:
-(IBAction)openUIActivityView:(id)sender {
UIActivityViewController *activityView = [[UIActivityViewController alloc]initWithActivityItems:#"Hello World" applicationActivities:nil];
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
[self presentViewController:activityView animated:YES completion:^{
}];
} else {
self.popup = [[UIPopoverController alloc] initWithContentViewController:activityView];
[self.popup presentPopoverFromBarButtonItem:sender permittedArrowDirections:UIPopoverArrowDirectionDown animated:YES]; }}
Just check if you already have a popup, and do something else
-(IBAction)openUIActivityView:(id)sender {
if (self.popup) {
[self.popup dismiss ...];
}
else {
// show popup code
}
}
The problem is, a view controller can only present one other view controller at any given time. Currently, when your tap the button a second time, you try to present a new instance of UIActivityViewController, while an other is already presented.
You can fix this, by checking the value of your main view controller's presentedViewController property. If it is not nil (and actually of type UIActivityViewController) you can return from the action without doing anything:
- (IBAction)openUIActivityView:(id)sender {
if (!self.presentedViewController) {
// We have no presented view controller yet.
// <your current code here>
} else {
// We are already presenting a view controller.
// Either dismiss it, or don't do anything.
}

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.

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;
}