Disabling ActionSheet dismiss on ipad when user touches outside popover - ios

I have an actionsheet showing in my app and it all work fine on the iphone. However, on ipad it automatically creates the actionsheet within a popover and I can't get it to disable the dimissing whe the user touches outside the actionsheet.
I have changed how the actionsheet is displayed for the ipad and is now shown using:
UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:#"" delegate:self cancelButtonTitle:#"Cancel" destructiveButtonTitle:nil otherButtonTitles:#"Choose a preloaded picture", #"Use a photo", nil];
actionSheet.actionSheetStyle = UIActionSheetStyleBlackOpaque;
actionSheet.tag = 1;
[actionSheet showFromRect:CGRectMake(100, 0, 300, 300) inView:self.view animated:YES];
[actionSheet release];
I have also tried using.
- (BOOL)popoverControllerShouldDismissPopover:(UIPopoverController *)popoverController
{
return NO;
}
Any idea on how I can stop the uiactionsheet popover from dismissing when a user touches outside the actionsheet?

Your popoverControllerShouldDismissPopover: method won't be called because it's a UIPopoverControllerDelegate method, and you're dealing with a UIActionSheet. Since the UIKit automatically creates the popover controller for you, you won't get a chance to set its delegate. You could access the popover view itself with [popoverActionsheet superview], but that won't give you the UIPopoverController.
From a user experience standpoint, Apple would ask you not to implement such behavior— if you need to present options in a modal fashion (where they don't go away until the user makes a choice) then the user will be more familiar with a UIAlertView, or a modally-presented view controller of your own.

Related

Getting warning for UIPopoverBackgroundVisualEffectView

I'm presenting a UIActionSheet in my view and one of the action sheet's buttons presents another action sheet. When I present the second action sheet on iPad I get this warning message in the logs:
UIPopoverBackgroundVisualEffectView is being asked to animate its opacity. This will cause the effect to appear broken until opacity returns to 1.
This is my code:
UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:#"Option"] delegate:self cancelButtonTitle:#"Cancel" destructiveButtonTitle:nil otherButtonTitles:#"Sort", nil];
actionSheet.tag = 1;
[actionSheet showInView:self.view];
And in the delegate:
-(void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
[self showSortAction];
}
-(void)showSortAction {
UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:#"Sort By" delegate:self cancelButtonTitle:#"Cancel" destructiveButtonTitle:nil otherButtonTitles:#"A-Z", #"Z-A", #"Newer to older", #"Older to newer", nil];
actionSheet.tag = 2;
[actionSheet showInView:self.view];
}
My guess is that the presentation of the second action sheet is causing the first action sheet's opacity to change, triggering the warning you're seeing. Instead of calling -showSortAction from within -actionSheet:clickedButtonAtIndex:, call it from within -actionSheet:didDismissWithButtonIndex:. This gives the first action sheet enough time to disappear from the screen before the second one starts animating in. (See the UIActionSheetDelegate documentation – specifically, the detail text for the clicked and did-dismiss methods.)
While we're on the subject, though, note that the UIActionSheet documentation says it's deprecated as of iOS 8. Unless you're programming against iOS 7 or older, consider transitioning to UIAlertController as soon as possible.
#Tim is correct above.
You shouldn't be using the deprecated UIActionSheet anymore. His solution about using actionSheet:didDismissWithButtonIndex: might have worked before but as per https://stackoverflow.com/a/25044710/1634905 it won't work anymore as Apple has moved on to UIAlertController.
You really should switch your code to UIAlertController instead of the old method to fix the issue.

UIActionSheet throws ViewController actionSheet:clickedButtonAtIndex

I am using XCode 5.1.1 for development purposes and using the iOS simulator to test my app.
I have an action sheet that has Save, Cancel and Delete options. I haven't yet coded the action handling for Save and Delete, but tapping on cancel should go back to the previous screen (I have used navigation controller to move between screens). But tapping on cancel, throws me "ViewController actionSheet:clickedButtonAtIndex:]: message sent to deallocated instance" error, which I am not able to solve. I have hooked up all the elements to their outlets/actions correctly. Below is my code. Please help. (I am trying to display the action sheet when a "return" button is clicked. And in the action sheet, when I tap cancel, the previous screen had to be displayed - I guess this can be done by dismissViewControllerAnimated which dismisses the current controller and displays the previous controller.)
-(IBAction)returnModalAction:(id)sender {
[self dismissViewControllerAnimated: YES completion: NULL];
UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:#"What do you want to do with the user values?"
delegate:self
cancelButtonTitle:nil
destructiveButtonTitle:#"Delete"
otherButtonTitles:#"Save", nil];
actionSheet.cancelButtonIndex = [actionSheet addButtonWithTitle:#"Cancel"];
[actionSheet showInView:self.view];
}
-(void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex{
if (buttonIndex == 2) {
NSLog(#"You have pressed the %# button", [actionSheet buttonTitleAtIndex:buttonIndex]);
}
}
On your method returnModalAction: you are dismissing the view, so the garbage collector will release all the references of self(the view controller) in memory that's why when you try to to show the action sheet [actionSheet showInView:self.view]; you get the error because the reference in memory doesn't exist.
So what you have to do is to perform [self dismissViewControllerAnimated: YES completion: NULL]; when you actually want to display the previous screen, in your case that would be on the actionSheet:clickedButtonAtIndex: method, based on the index of the button.

Asking for user confirmation before leaving a view in iOS

I need to show an UIAlertView before a user leaves a certain view, either by tapping a 'back' navigation bar button or by tapping one of the tab items in the tab bar I have, in order to ask him for confirmation. It would be a two-button alert, a 'Cancel' one to stay in the view, and an 'Accept' one to leave. I need to do this because I have to make the user aware that unsaved changes will be lost if leaving.
I tried to do this by creating and showing the alert view in the viewWillDisappear: method:
- (void)viewWillDisappear:(BOOL)animated
{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:NSLocalizedString(#"Exit", #"")
message:NSLocalizedString(#"Are you sure you want to leave? Changes will be discarded", #"")
delegate:self
cancelButtonTitle:NSLocalizedString(#"Cancel", #"")
otherButtonTitles:NSLocalizedString(#"Accept", #""), nil];
[alertView show];
[super viewWillDisappear:animated];
}
But the view is pop anyway, and the alert view is shown after that and app crashes since its delegate is the view controller that has been already pop from the navigation stack... I don't find the way to solve this scenario, can anybody help me?
Thanks!
Showing the alert view when viewWillDissapear won't work, because the view is already dissapearing, it's on its way to be removed.
What you can do, is add yourself a custom action when the back button is pressed, then you decide what to do when the back button is pressed, you can show the alert view, and then in one of the buttons procedd to dismiss the view, something like this:
- (id)init {
if (self = [super init]) {
self.navigationItem.backBarButtonItem.target = self;
self.navigationItem.backBarButtonItem.action = #selector(backButtonPressed:);
}
return self;
}
Then show the alert view when the back button is pressed:
-(void)backButtonPressed:(id)sender
{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:NSLocalizedString(#"Exit", #"") message:NSLocalizedString(#"Are you sure you want to leave? Changes will be discarded", #"") delegate:self cancelButtonTitle:NSLocalizedString(#"Cancel", #"") otherButtonTitles:NSLocalizedString(#"Accept", #""), nil];
[alertView show];
}
Now, when the confirmation button in the alert view is pressed, just call:
[self.navigationController popViewControllerAnimated:YES];
Or do nothing if the user cancels
I would be tempted to move the data manipulation you're trying to protect into a modal view controller and handle the validation on whatever action you choose to have dismiss the modal presentation. To me, that's the point of modal: something that has to be completed before interacting with the rest of the app.

Actionsheet on ipad

This code works fine on iPhone, but on iPad the actionsheet is showing up in the middle of screen. not in the corner where the trigger button is. Also there is no cancel buttons showing up.
Also, the app is only in landscape mode. The camera trigger button is on top right corner of the screen.
mediaPicker = [[UIImagePickerController alloc] init];
mediaPicker.allowsEditing = YES;
if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:nil
delegate:self
cancelButtonTitle:#"Cancel"
destructiveButtonTitle:nil
otherButtonTitles:#"Take photo", #"Choose Existing", nil];
//[actionSheet showInView:self.view];
[actionSheet showInView:self.navigationController.view];
}
// If device doesn't has a camera, Only "Choose Existing" and "Cancel" options will show up.
else {
UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:nil
delegate:self
cancelButtonTitle:#"Cancel"
destructiveButtonTitle:nil
otherButtonTitles:#"Choose Existing", nil];
[actionSheet showInView:self.view];
}
I need actionsheet to show up where the button is. How should I go about it.
IPAD have some special rules about actionsheets and their cancel buttons, it usually depends on where you are displaying the actionsheets from
You can show an action sheet from a UIToolbar, UITabBar,UIBarButtonItem, or from a UIView. This class takes the starting view and current platform into account when determining how to present the action sheet. For applications running on iPhone and iPod touch devices, the action sheet typically slides up from the bottom of the window that owns the view. For applications running on iPad devices, the action sheet is typically displayed in a popover that is anchored to the starting view in an appropriate way. Taps outside of the popover automatically dismiss the action sheet, as do taps within any custom buttons. You can also dismiss it programmatically.
When showing an action sheet on iPad, there are times when you should not include a cancel button. If you are presenting just the action sheet, the system displays the action sheet inside a popover without using an animation. Because taps outside the popover dismiss the action sheet without selecting an item, this results in a default way to cancel the sheet. Including a cancel button would therefore only cause confusion. However, if you have an existing popover and are displaying an action sheet on top of other content using an animation, a cancel button is still appropriate
instead of using showInView try using from one of these method
[actionSheet showFromBarButtonItem:barItem animated:YES];
Explaination is here & here
On the iPad, don't use showInView:. Instead, use one of the other more specific methods for showing the action sheet such as showFromBarButtonItem:animated:.
As noted in nsgulliver's answer, it is normal for action sheets to not display the Cancel button on an iPad. All the user needs to do is tap outside the action sheet. This is treated the same as canceling.

Showing a UIActionSheet from a UIBarButtonItem trouble

So I set up my storyboard files and whatnot and made an action for my UIBarButton. In this method I create an action sheet and display it. But it seems no matter what I do it always displays from the bottom. Can't you get it to pop down from the navbar with a little arrow in the border that points to the button you clicked? Well, I'm having trouble finding the right method. Here's what I have tried:
- (IBAction)actionBtnPressed:(UIBarButtonItem *)sender {
UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:#"Options" delegate:self cancelButtonTitle:#"Cancel" destructiveButtonTitle:#"Report User" otherButtonTitles: nil];
actionSheet.actionSheetStyle = UIActionSheetStyleBlackOpaque;
[actionSheet showFromBarButtonItem:sender animated:YES];
}
I have also tried
[actionSheet showFromToolbar:self.navigationController.toolbar];
instead of showFromBarButtonItem, but these methods aren't reading my mind that I want it to display from the button. Any ideas?
For an iPhone, actionsheet is always displayed from bottom of the screen. What you meant is a UIPopover which is available only for an iPad. However, you can use a custom popover if you want to use the same feature in your app. Check WEPopover. It allows you to create pop over in iPhone and it can be safely used to draw popover in your app. You can show the required items inside this popover.

Resources