nilMy app needs to run a few checks then maybe display a UIAlertView any time my application becomes active. To do this I have registered for didBecomeActiveNotification and run my check here.
The issue occurs whenever my UIAlertView pops up during the initial launch, it causes the "Applications are expected to have a root view controller at the end of application launch" message. I'm guessing this is happening because UIAlertView is shown before viewDidAppear:.
How should I be triggering my UIAlertView if not in didBecomeActiveNotification?
2012-03-16 12:21:47.238 App[4181:707] viewDidLoad:
2012-03-16 12:21:47.462 App[4181:707] didBecomeActiveNotification:
2012-03-16 12:21:47.793 App[4181:707] Applications are expected to have a root view controller at the end of application launch
2012-03-16 12:21:48.500 App[4181:707] viewDidAppear:
Edit: To trigger this in a new project do the following.
1 New Project -> Single View Application
2 In Viewcontroller.m add the following to viewDidLoad:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(didBecomeActiveNotification:) name:UIApplicationDidBecomeActiveNotification object:nil];
3 In ViewController.m add the following method
-(void)didBecomeActiveNotification:(NSNotification *)notification
{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:nil message:nil delegate:self cancelButtonTitle:#"ok" otherButtonTitles: nil];
[alertView show];
[alertView release];
}
4 Build and Run
Just as i said, no console messages, nor compiler-warning.
Can you confirm that the message is only visible with the alert shown and there's no such message without an alert ?
How should I be triggering my UIAlertView if not in didBecomeActiveNotification?
I would trigger it in the root view controller.
Your AppDelegate can't launch UIAlertViews. That can only be done by a ViewController.
I have a similar thing in one of my apps, where I want to check something whenever the app launches or becomes active, and present a modal viewController to the user.
What I did was to create a new class, that has one method:
+(BOOL)checkIfShouldShowAlert
You can then call this method in each of your viewControllers' viewWillAppear methods, like so:
if ([myCheckerClass checkIfShouldShowAlert]) {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle...
[alertView show];
}
By using a separate class, it makes your code a lot simpler to update in the future!
Related
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.
I presented a view controller with the presentModalViewController:animated: method. Let's call this view myView. On myView, I have a button and when tapped, it is supposed to create a UIAlertView and show it.
However, when I tap this button, the alertView is created but doesn't appear on top of myView. Instead, it is hidden behind myView.
Note: To verify that the alertView is hidden behind myView, I added a second button on myView and when it is tapped, the myView view controller dismisses itself (i.e. [self dismissModalViewControllerAnimated:YES]). When myView is dismissed, the alertView appears.
Any idea why the alertView is hidden behind myView?
I think after you show UIAlertView you are adding a subview on UIWindow. And it's above UIAlertView in UIWindow layer.
Make sure you don't add anything on UIWindow as it is not a good practice.
If you still wan t to carry out adding subview just send that subviewToBack:
Best of luck
I have the same problem. In Controller1, I present Controller2, and in Controller2 I create an alertView. But the alertView is put behind the Controller2's view.
I found that if I create the alertView not from any viewController(by using a public function), the alertView will appear at the front of the screen.
+ (void)alertShow:(NSString *)alertMsg {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:alertMsg
message:nil delegate:self
cancelButtonTitle:nil
otherButtonTitles:nil];
[alert show];
[self performSelector:#selector(dismissAlertView:) withObject:alert afterDelay:2.5];
}
Hope it will solve your problem.
I was having this problem, also. It was intermittent, but I believe it related to displaying the alert on a background thread.
As of Xcode 9, Swift 4:
To test if function is running on main thread, use: Thread.current.isMainThread
print("On main thread: \(Thread.current.isMainThread)")
To display alert, or perform any operation on main thread, use OperationQueue.main:
OperationQueue.main.addOperation {
// code to perform on main thread
}
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.
I am really stumped on this one issue I am having.
So we have an app that allows the user to sync some photos to our server. So I accomplished that with a dispath_async call and it works, however I would like to tell the user that it has finished weather successful or not.
So my question is how do I show the user an AlertDialog box, when I DO NOT know what view they will be on(since it's async I don't want them to have to stay on that ViewController, but move around the app and do other things while that is being uploaded).
I have tried:
[self showAlertView]; but that crashes the app because I am no longer on that view.
Than I tried:
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertView* alert = [UIAlertView withTitle ....]; // you get the idea, just create a simple AlertView
[alert show]; // also tried: [alert performSelectorOnMainThread:#selector(show) withObject:nil waitUntilDone:NO]; here and that didn't work either
});
Oh and LocalNotifications aren't going to work because if they are in the app it doesn't show up(Android has iOS beat on this front, it would be a lot easier to have that just work when the user is in the app too, then I wouldn't have to worry about all of this).
Any ideas on how I can accomplish my goal.
You can use UILocalNotification even if the app is running in the foreground but you will have to handle the notification your self. You can just display an simple AlertView.
Just add this to the app delegate class:
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification {
UIAlertView* alert = [[UIAlertView alloc] initWithTitle:notification.alertBody message:nil delegate:nil cancelButtonTitle:#"Oke" otherButtonTitles:nil];
[alert show];
}
You can display a modal view on the current controller by accessing the controller through your application's window.
UIWindow *window = [UIApplication sharedApplication].keyWindow;
UIViewController *controller = window.rootViewController;
// Use controller to display your window.
Following are the code for displaying alert in a view controller
-(void)saveProducts {
pData = [[JsonModel sharedJsonModel] prodData];
if ([pData count] == 0 && [self respondsToSelector:#selector(alertView:clickedButtonAtIndex:) ] ) {
alert = [[UIAlertView alloc]initWithTitle:#"Alert" message:#"No products against this category" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
}
[self.tblView reloadData];
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex == 0) {
[self.navigationController popViewControllerAnimated:YES];
[actInd stopAnimating];
}
}
But in slow network, alert will come slowly. If we click on back button of navigation bar at the mean time, pop the navigation controller and displaying alert in new view controller. But when i clicks on OK, app suddenly crashes with EXC_BAD_ACCESS error.
I also tried
didDismissWithButtonIndex
function instead of
clickedButtonAtIndex
But same error occurs. Please help me
It works normally if we didn't click on back bar button. Problem only arises when first view controllers alert displays in second view controller
EDIT
This is the error report
* -[ProductsListing alertView:didDismissWithButtonIndex:]: message sent to deallocated instance 0x8478280
EDIT
I understand the problem. When I click on back button, my alert delegate deallocates and delegate calling results error. How can I overcome this?
My best guess is that either 'self.navigationController' or 'actInd' have already been released. Also, your 'UIAlertView' leaks memory (unless you're using ARC). Profile the app using Instruments, selecting the "Zombies" tool and see what it comes up with.
I believe you have to change
[alert show];
to
if(self.view.window){
[alert show];
}
This way the alert appears only if the controller(the view) is still on screen.(why let the user see an alert from a previous screen?)
If you want the alert to appear anyway....then the "old" controller must inform the "new" one that a problem occurred...and now its the new controller's job to inform the user.
Or you can try changin this part
[self.navigationController popViewControllerAnimated:YES];
[actInd stopAnimating];
to
if(self.view.window){
[self.navigationController popViewControllerAnimated:YES];
[actInd stopAnimating]; // im not sure where the animation is...so not sure if this shoulb be in here or not
}
From what you described the problem here may be this(a wild guess)
[actInd stopAnimating];
called after the viewController is removed(popped).the actInd may not have a valid memory and hence it crashes
change the method content like this and check
if (buttonIndex == 0) {
[actInd stopAnimating];
[self.navigationController popViewControllerAnimated:YES];
}