I apologize if this has been asked but I can't seem to find it anywhere. I even recreated my issue in a demo project in case any of you want to see it first-hand, although I don't know where I should post it.
I have a xibless UINavigationController based app. Some of my child ViewControllers have a button on the right side at the top that then displays a UIActionSheet. My app is designed for iPhone and iPad, so when I get ready to display the UIActionSheet I do:
UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:[NSString stringWithFormat:#"%# Menu", [self title]] delegate:self cancelButtonTitle:#"Cancel" destructiveButtonTitle:nil otherButtonTitles:#"Email", #"Print", nil];
[actionSheet setActionSheetStyle:UIActionSheetStyleDefault];
if ([actionSheet respondsToSelector:#selector(showFromBarButtonItem:animated:)])
[actionSheet showFromBarButtonItem:[[self navigationItem] rightBarButtonItem] animated:YES];
else [actionSheet showInView:[self view]];
[actionSheet release];
On iPad, I'm trying to show the UIActionSheet attached to the right bar button and on iPhone it should slide in from the bottom. All of this works beautifully.
Unfortunately, if you tap the button and show the menu on iPad, but then tap the back button on the top left side of the app, the menu doesn't dismiss. Instead UINavigationController dutifully pops back and the UIActionSheet is still there. If you try to tap something on the menu you of course get a crash. If the user would have tapped anything else on the screen instead of the Back button, the menu properly dismisses.
If you try this test on iPhone, everything works as expected. There is no issue.
My demo project has an AppDelegate and a ViewController and that's about it. The AppDelegate builds an NSDictionary of NSDictionaries just so I have a model I can recurse through to demonstrate the issue. The ViewController shows all of the keys of the dictionary and if the corresponding value is an NSDictionary, you can tap it to drill down.
This is an interesting problem. Here's what the UIActionSheet Class Reference has to say.
On iPad, this method presents the action sheet in a popover and adds
the toolbar that owns the button to the popover’s list of passthrough
views. Thus, taps in the toolbar result in the action methods of the
corresponding toolbar items being called. If you want the popover to
be dismissed when a different toolbar item is tapped, you must
implement that behavior in your action handler methods.
So when you display the action sheet, it's automatically creating a UIPopoverController and set the containing toolbar (or navigation bar) as the popover's passthrough views, allowing touch events to continue. I think the best bet is to create an instance variable for your action sheet and to force it to dismiss if it is visible in -viewWillDisappear:.
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
if (self.actionSheet.window) // If action sheet is on screen, window is non-nil
[self.actionSheet dismissWithClickedButtonIndex:self.actionSheet.cancelButtonIndex animated:animated];
}
Have you tried force-dismissing the ActionSheet on viewWillDisappear?
Try this:
// In MyViewController.m
- (void)viewWillDisappear:(BOOL)animated {
[actionSheet dismissWithClickedButtonIndex:nil animated:animated];
}
*The crash sounds like a possible EXC_BAD_ACCESS. You might be losing your pointer reference to 'actionSheet' when you change views due to your release. Might be good to hang on to a reference to actionSheet in your .h file and manage the timing of your release.
*Also see the docs for info about the dismiss message: http://developer.apple.com/library/ios/#documentation/uikit/reference/UIActionSheet_Class/Reference/Reference.html
Related
I have an app originally developed for iPhone, with a MapViewController as the main screen, and a Help screen and Tutorial screen both called from within a UI ActionSheet. Works fine for the iPhone.
When run on an iPad, I can't get to the Help screen and get the following runtime error:
Warning: Attempt to present <HelpViewController: 0x177076c0> on <MapViewController: 0x177d7060> which is already presenting <UIAlertController: 0x17732620>
If I add the following line of code
[self dismissViewControllerAnimated:YES completion:nil];
then I do get to the Help screen, but with the following error:
Warning: Attempt to dismiss from view controller <MapViewController: 0x17d75660> while a presentation or dismiss is in progress!
When I exit the Help screen, the MapView does not come up, however selection a new baseMap brings it back to life.
So, the code runs fine on an iPhone. On the iPad I get an error that says
1) Don't open a new view with the UIAlertController active, or
2) Don't dismiss the view, because a dismiss is already in progress...
Sure seems like a timing problem to me, I've tried both a "sleep" statement and some code to provide a short delay, neither have helped.
Anyone have any ideas?
you have to set action sheet for iPad like below..
UIActionSheet *popup = [[UIActionSheet alloc] initWithTitle:#"Some Title" delegate:self cancelButtonTitle:#"Cancel" destructiveButtonTitle:nil otherButtonTitles:nil,nil];
if(!IS_IPad){
[popup showInView:[UIApplication sharedApplication].keyWindow];
}else{
[popup showFromRect:CGRectMake((CGRectGetWidth(self.view.frame)-200)/2, self.imgLogo.frame.origin.y,200, 200) inView:self.view animated:YES];
}
Surly your action sheet working in both.
This warning comes when you are trying to present/dismiss a view with animation while another animation is still in progress. This might be because while making a selection from UIActionsheet it animates and fades away and at the same time another viewcontroller is appearing with animation. It might help if you dismiss the view controller after delay of 0.2 seconds
On an iPhone running iOS 8, the code below causes an action sheet to pop up. However, on an iPad running iOS 8 the code below does not cause an action sheet to pop up and instead nothing happens.
NSUserDefaults *defauj = [NSUserDefaults standardUserDefaults];
NSArray *cod = [defauj objectForKey:#"customlistofstuff"];
UIActionSheet* actionSheet = [[UIActionSheet alloc] init];
actionSheet.delegate = self;
for(int i=0;i<[cod count];i++)
{
[actionSheet addButtonWithTitle:[cod objectAtIndex:i]];
}
actionSheet.cancelButtonIndex = [actionSheet addButtonWithTitle:#"None"];
[actionSheet showInView:[UIApplication sharedApplication].keyWindow];
Try this:
[actionSheet showInView:[UIApplication sharedApplication].keyWindow.rootViewController.view];
It looks like you can't present action sheets on UIWindows directly anymore, you have to present them on an actual view that is managed by a view controller, so the root view controller's view is perfect for this.
I think this has less to do with the fact that UIActionSheet is deprecated (and you can't just magically switch to UIAlertController just yet if you need to support iOS 7), and more to do with the way their presentation is handled in the underlying implementation — I'm guessing it now relies on the view the sheet is presented in having a view controller, which is not true for windows.
EDIT: If you have a view controller presented modally over the top of the root view controller, this obviously won't work as the root view controller's view is no longer visible. You'll need to present the sheet in a view that is currently visible, e.g. the view of the current view controller (self.view).
I bet this has something to do with UIActionSheet being deprecated in iOS 8. You're supposed to use UIAlertController instead with a preferredStyle of UIAlertControllerStyleActionSheet.
Try using that instead and see if it works. You'll have to use blocks instead of methods, but that shouldn't be too hard to do.
According to Apple's Human Interface Guidelines about Temporary Views, A cancel button should only be used when the view presenting the action sheet is a popover, because, according to the HIG, users can tap outside the popover to dismiss the action sheet.
Therefore, cancel buttons do not work on iPads.
UIActionSheet has also been deprecated, and you should use UIAlertController instead.
I am trying to create a UIActionSheet inside a UIViewController as follows:
discardAS = [[UIActionSheet alloc] initWithTitle:"title" delegate:self cancelButtonTitle:"Cancel" destructiveButtonTitle:"Discard" otherButtonTitles:nil];
discardAS.actionSheetStyle = UIActionSheetStyleDefault;
[discardAS showInView:[self view]];
When I evoke it - the entire screen "dim" (as if the actionsheet is actually in the foreground) - but I see no actionsheet.) It appears to be showing "offscreen".
WHen I try this on iOS7, I can see a white box shooting upwards, quickly going offscreen - as if it the ActionSheet is being placed above the top of the screen. When I click on the window, the dimness goes away, and all is normal - like iOS7 does if you tap out of the actionsheet and it goes away.
Thus, I am concluding the ActionSheet is being presented ABOUT (and out of) the confines of the screen.
I am also seeing the following on the console
Presenting action sheet clipped by its superview. Some controls might not respond to touches. On iPhone try -[UIActionSheet showFromTabBar:] or -[UIActionSheet showFromToolbar:] instead of -[UIActionSheet showInView:].
Both of which I tried, with the same results. Displaying self.view.frame shows a rect about 0,0,300,200 - so It's not grotesquely oversized.
Any idea why, or how to fix/debug?
For posterity - the answer was I had an "Extra View Controller" in my heirarchy - as described here: iOS 6: What changed that made this View Controller Hierarchy break?
I have an UIViewController. Within it the user may launch an UIActionsheet or a popover. If the device rotates, the UIViewController is intentionally released. The problem is that the UIActionsheet / popover lives on and becomes a zombie to haunt me afterward. The user may click a button in the zombie and crash the application. I tried to call [actionsheet dismissWithClickedButtonIndex:0 animated:NO] in dealloc or shouldAutorotateToInterfaceOrientation. No effect. Appreciate any suggestions. Thanks.
I found the solution from this post: [dismiss actionsheet][1]
[1]: iPad crash with UIActionSheet displayed from child view controller. I made a mistake trying to recall the actionsheet by searching through the UIViewController view's subviews or calling the viewWithTag method. Strangely enough, it did return a non-nil object. But using this object to call dismissWithClickedButtonIndex does not work.
The autor of the post suggests using an UIActionSheet pointer and dismiss the actionsheet in viewWillDisapper works. For the above specific application, I found it worked better to dimiss the actionsheet in dealloc.
I have a UIBarButtonItem opening an action sheet to offer users choices about what to do. Everything works as expected unless I try to click on the "Cancel" button. The target of the button appears to have moved up from where it should be. I can only activate it by clicking somewhere in the middle of the "Cancel" and "Ok" buttons.
I've tried at action sheets in other applications and they work fine, so it's not just my big thumb. The action sheet is opening in a UIViewController
- (void)showOpenOptions
{
UIActionSheet *sheet = [[UIActionSheet alloc]
initWithTitle:NSLocalizedString(#"Open link in external application?", #"Open in external application")
delegate:self
cancelButtonTitle:NSLocalizedString(#"Cancel", #"Cancel")
destructiveButtonTitle:NSLocalizedString(#"Open Link", #"Open Link")
otherButtonTitles:nil];
[sheet showInView:self.view];
[sheet release];
}
Instead of passing the current view controller's view to the action sheet, use the showFromTabBar: method of UIActionSheet.
The Right Way
This will give the correct tappable area:
[actionSheet showFromTabBar:self.tabBarController.tabBar];
The Wrong Way
This will put the tappable area in the wrong place (if you're using a tab bar or toolbar):
[actionSheet showInView:self.view];
If you're using a toolbar, use the showFromToolbar: method instead. You'll need a reference to the toolbar, most likely an ivar
[actionSheet showFromToolbar:self.myToolbar];
My Old Answer Also works, but is hacky:
Just found a possible answer:
01-Dec-2008 10:22 PM Tom Saxton:
I looked at this bug some more, and it seems to be an issue with the tabbar.
If you call UIActionSheet's [sheet showInView:self.view] from a view controller that is a child of a UITabViewController, then the hit testing on the cancel button fails in that portion of the UIActionSheet that lies above the tabbar's view.
If you instead pass in the UITabBarController's view, then the UIActionSheet acts as expected.
NOTE: in iPhone OS 2.1 and earlier, the UIActionSheet came up from the top of the tab bar when you pass the child view, but in 2.2, it comes up from the bottom of the tab bar, and thus covers the tab view.
http://openradar.appspot.com/6410780
Edit: It works correctly when I change the view to be the tab bar's view
[sheet showInView:self.parentViewController.tabBarController.view];
I found an answer over here that works.
using: [filterActionSheet showInView:[self.view window]];
i tried a few ways to get to my tab bar and they way this app is set up it seem convoluted...
Instead use:
[sheet showFromTabBar:theTabBar];
Here is the fix.Try this:
[actionsheet showInView:[UIApplication sharedApplication].keyWindow];
I think a combination of three of the answers is the right way of handling this:
[actionSheet showFromTabBar:self.tabBarController.tabBar];
i.e., use showFromTabBar (that's why it exists) and you don't need the parentViewController as Nathan pointed out (in fact, self.parentViewController.tabBarController.tabBar returns nil for me.
FYI - had the same problem with UIDocumentInteractionController's actionsheet stepping on the tabbar. Used the following to fix.
UIViewController *parentView = [[self parentViewController] parentViewController];
[docController presentOptionsMenuFromRect: rect inView: parentView.view animated:YES];
write simplite code
actionSheet.actionSheetStyle = UIActionSheetStyleDefault;
this work fine