iOS: UIActionsheet (or UIPopoverController view) becomes zombie - ios

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.

Related

Detect popover dismiss in iOS8

in my iPad-app I connected a UIButton to another UIViewcontroller by just dragging in Storyboard and chose popover as segue. Everything is working fine, but the user can dismiss the popover by touching just somewhere besides the popover right.
How can I detect that the popover has dismissed in iOS8? In iOS7 I could just use the UIPopoverDelegate -popoverDidDidmiss...
But this is not working anymore!? I googled a lot but could not find any solution.
You put your
-(void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController
in the UIViewController where the start UIButton is ? (not in another popover UIViewcontroller ?)
That work well for me with iOS 8.1...
You have to delegate to the initial UIViewController for that.
I assume you set the delegate properly, but do you retain the popover, i.e. assign it to a strong property? In ios7 if you didn't retain the popover you would get exception: '[UIPopoverController dealloc] reached while popover is still visible.' In ios8 this is not longer the case, so you get the working popover and you can dismiss it, but the delegate methods are not called anymore.
(Frankly speaking, I'm not sure why this is so. I'd suppose that at least "should dismiss popover" should be called anyway).
You should probably use UIPopoverControllerDelegate and the method
popoverControllerDidDismissPopover:
to accomplish what you need.
in iOS8, it is using the UIViewController's UIPopoverPresentationController to present the popover. (Optionally you still can use back the old UIPopoverController to build the popover manually.
If you are using storyboard on iOS8, you can set the UIViewController's popoverPresentationController delegate to handle the followings:
popoverPresentationControllerShouldDismissPopover:
popoverPresentationControllerDidDismissPopover:

Prevent UIActionSheet from dismissing on ipad

I have a universal app that uses a UIActionSheet. The selection is mandatory, which works fine on the iphone, but on iPad it is presented as a popover, so tapping outside of it dismisses it, which I don't want to allow.
I know that there is a delegate method for UIPopoverController I could use, but I don't think I can get access to the UIActionSheet's popover controller to set the delegate etc...
Anyone know how I can do this?
Thanks in advance.
It's possible using undocumented APIs, see
See Accessing UIPopoverController for UIActionSheet on iPad about changing passthroughViews for UIPopoverView which is an undocumented ancestor of UIActionSheet.
Another solution would be to override [UIApplication sendEvent:] and filter the touches coming outside the action sheet.
I recommend you to use a different widget for modal selection. Users don't expect UIActionSheet to be modal, so it would be only confusing to them.

presentViewController is disabling all removeFromSuperview messages in a UISplitViewController

Here is a visual representation of the UISplitViewController:
I'm trying to present a UIViewController (myVC) that's a subview of a UIView inside the DetailViewController of a UISplitViewController using this:
[appDelegate.splitViewController presentViewController:myVC animated:NO completion:nil];
It presents, I dismiss it, the app rotates without a hitch, then when I try to reload the detail view by clicking on a cell in the rootViewController (a UINavigationController), all the [orangeUIView removeFromSuperview] messages are mysteriously disabled (there are more orange UIViews hidden here that are inside a mainScrollView). The "INFO LABEL" in the topToolbar is updating, so I know the cell is sending data...
Why would this activity disable removeFromSuperview if presented using splitViewController? I've presented myVC using the detailViewController...
[appDelegate.detailViewController presentViewController:myVC animated:NO completion:nil];
which doesn't disable removeFromSuperview, but causes an assortment of other rotational crashes.
What's going on here?
Since this question was created as a result of a previous question, the answer to this problem was...try something else. I was unable to fix this problem by presenting a view controller, most likely because I'm using a custom UISplitViewController with a hierarchy I'd rather not get into.
Here is the solution I came up with:
Core Plot - Options on making the graph full screen - addSubview after presenting modal problems

alertView:didDismissWithButtonIndex: message sent to deallocated instance

The problem is only happening with iOS4.3. I'm using ARC and my Base SDK is iOS6.
In -viewDidAppear of my view controller, I check if this is the first time the app has been started and if so, then I create and show a UIAlertView. I assign that UIAlertView to a strong property on the view controller and set self as the UIAlertView delegate.
self.uiAlertView = [[UIAlertView alloc] initWithTitle:#"Welcome!"
message:messageString
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:#"View Tutorial Videos", #"Email Support", nil];
When I tap one of the buttons, the app crashes complaining that -alertView:didDismissWithButtonIndex: was sent to a deallocated instance. The delegate is the view controller that is displaying the UIAlertView.
On all subsequent launches of the app, when the UIAlertView isn't shown there are no problems. The view controller is definitely not being deallocated.
If I display the UIAlertView but set the delegate to nil, then there is no problem and the app continues working, so clearly the view controller hasn't been deallocated because I can keep using it.
What is happening? This only causes a problem with iOS4.3.
EDIT: Based on suggestions in the comments, I added some more log messages in different places.
I've discovered that the view controller IS getting dealloc'd, but only if that view controller displays the UIAlertView. What in the world would cause the view controller to get dealloc'd just because it sets itself as the delegate of a UIAlertView and then displays it?
My app delegate has a strong reference to the view controller, so there is absolutely no reason that I can see for the view controller to get dealloc'd.
EDIT 2: I've discovered that during start up my main view controller is being instantiated TWICE. The first one is the one creating the UIAlertView and that one is getting dealloc'd. The second one is the one that I've been able to interact with afterwards that made me think the view controller was still there and operable.
However, I can't figure out where or why my view controller would be created twice. I don't have any alloc/init statements for the view controller. It only exists in the MainWindow_iPhone.xib.
The first time viewDidLoad is called on my view controller, the stack frame above is [UIViewController view]. The second time viewDidLoad is called on the second instance of my view controller, the stack frame above is [UINib instantiateWithOwner:options:]
EDIT 3: I've "fixed" the problem, but I don't understand why this would happen. Perhaps you can help me understand.
In my MainWindow_iPhone.xib, I created my root view controller and assigned it to an IBOutlet on my app delegate. Instead, I deleted the view controller from the xib and created it in code in the -application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions ... and the problem disappeared.
Why in the world would the view controller be created twice when in the xib?
I have this issue before. alertView:didDismissWithButtonIndex: is called after alertView: clickedButtonAtIndex:. You most likely deallocate the view controller in alertView:clickedButtonAtIndex by doing something like [self.navigationController popViewControllerAnimated:YES].
UIAlertView delegate is assign not weak reference. When delegate is deallocated, it is not set to nil automatically. That's reason why your code is crashed.
i fixed this issue commenting this method (or deleting it).
- (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex
{
NSLog(#"se apreto cancel");
}
as James Wang said, the didDismissWithButtonIndex is called after clickedButtonAtIndex so i commented it to avoid the crash.

iPad crash with UIActionSheet displayed from child view controller

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

Resources