I'm pushing a view on to my navigation controller -
[self.navigationController pushViewController:_gameOverViewController animated:YES];
In the new view's viewDidAppear, I show an alert view -
UIAlertView* alert = [[[UIAlertView alloc]
initWithTitle:#"alert title"
message:#"some text"
delegate:self
cancelButtonTitle:#"Rate It!"
otherButtonTitles:#"No Thanks",
#"Don't ask again", nil] autorelease];
[alert show];
In iOS 6 and earlier this works fine. The 'gameOver' view is visible behind the alert view. Once the alert view is dismissed the game over view is visible. In iOS 7, the alert view shows over the previous view. When the alert view is dismissed, the previous view is still visible. The 'gameOver' view is never presented to the user.
Is there a better way to show UIAlertView in iOS 7?
It came down to popping a view off the navigationcontroller and pushing a new one on at the same time. in ios6 and below this worked fine. in ios7 it appears that you cant push a new view onto the stack until the previous animation has stopped.
Related
I Have this code:
else if ([response isEqualToString: #"1"])
{
UIAlertView *alertView1 = [[UIAlertView alloc] initWithTitle:#"FAILED" message:#"Please, Try Again" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alertView1 show];
}
I catch from a JSON response an error and show this alert.
This code is into a post method of a ViewController with table view, and this view controller is a parent to another view controller. The problem is that AlertView not showing (or showing fast) in the present view (the title in navigation bar become dark), but if I going back to the parent view controller and return to this view (practically, if i "refresh" the view), the alert show correctly.
I have the same code for iPad and iPhone, but in iPad not done.
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've a problem with some UIAlertView I instance in a view controller's method. This UIViewController is presented from another UIViewController, and at a certain point I display an UIAlertView from an instance method this way:
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:NSLocalizedString(#"Upload Error", #"")
message:NSLocalizedString(#"It was not possible to complete the upload, do you want to try again?", #"")
delegate:self
cancelButtonTitle:NSLocalizedString(#"Cancel", #"")
otherButtonTitles:NSLocalizedString(#"Accept", #""), nil];
[alertView show];
I can dismiss the presented UIViewController before or at the time this alert view is shown, what causes that such alert view is shown when the presented view controller is not on screen anymore, so the alert view is shown in top of the presenting view controller. Then, when tapping one of the alert view's buttons, app crashes, I guess because its delegate was the presented view controller and it does not exist. How could I avoid this situation?
Thanks!
Either dismiss the alert view by calling dismissWithClickedButtonIndex:animated: before your presented view controller is about to be dismissed, possibly in its viewWillDisappear: or an appropriate method. Or if you want the alert view to remain on screen even after presented view controller is gone, pass the presenting view controller reference to the presented view controller and set the alertView's delegate to the presenting view controller.
The answer depends on the behavior you want to achieve.
First check UIAlertView documentation:
delegate: The receiver’s delegate or nil if it doesn’t have a delegate.
If you don't want to do 'something special' depending on the user
action, pass null.
If you want to do 'something special' depending on
the user action create a special delegate to handle the user action and keep that one alive.
In an iOS run on iOS 6.1 (emulated) and 6.1.3 (physical device) application we show an UIActionSheet from a view with the following code:
UIActionSheet *actionCreateNewComment = [[UIActionSheet alloc] initWithTitle:CLocalised(#"EditExistingComment") delegate:self cancelButtonTitle:CLocalised(#"No") destructiveButtonTitle:nil otherButtonTitles: CLocalised(#"Yes"), nil];
[actionCreateNewComment setActionSheetStyle:UIActionSheetStyleBlackOpaque];
[actionCreateNewComment setTag:ActionSheetTagNewComment];
[actionCreateNewComment showFromToolbar:self.navigationController.toolbar];
[actionCreateNewComment release];
The View sets the toolbar with:
UIBarButtonItem *item = [[UIBarButtonItem alloc] initWithCustomView:control];
[self setToolbarItems:[NSArray arrayWithObject:item]];
Where control is an UISegmentedControl.
The code for handling the button actions are:
-(void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex{
bool shouldAddNewComment = buttonIndex == [actionSheet cancelButtonIndex];
[self navigateToCommentScreen:shouldAddNewComment];
}
Every time this action sheet is shown the tool bar will go "blank". Not only for this view but also for the other views.
Here is an example of a work flow:
Note that if I navigate from Instr to Comment without the UIActionSheet, but otherwise the same code, the problem will not appear.
The problem here is that in the "Instr" view the toolbar is empty.
If I pop two more views to return to "List" its toolbar will be empty as well:
Even if I then navigate back to WO and Instr, by allocing and init new forms, and then pushing them, the toolbars will still be empty.
There is another navigation option from the "List" View that shows a "Summary" view. Depending on the data this view will show with or without buttons in the toolbar:
Both "Summary Button" and "Summary empty" are intended views. "Summary hidden" is shown after the UIActionSheet have been shown before navigating between Inst and Comment.
But if I navigate to a "Summary" view that is shown as "Summary Empty" in the picture (intended, due to the data) then the toolbar will start working everywhere again. Or at least until I show the UIActionSheet from the Instr view again.
There are some, in my opinion, strange things about this:
If I navigate to a view where the toolbar is empty, which hides the toolbar, see pic: "SummaryEmpty", then the toolbar will show as intended in the other views.
We use the same code to show an action sheet in other views of the application without any problem
If I run the application on an emulated iOS 5.1 or emulated iOS 5.0 the problem does not appear
Have I missed something when it comes to dismissing the action sheet or how to show it?
Please let me know if you want more information.
Update:
If I forcibly call [self setToolbarItems:nil] after the action sheet button event and then update it with the same items as before the Tool bar will show up as intended.
This does not feel as the right way to do it.
Update 2013-05-28:
Thanks to #LOP_Luke I found out that if I do not navigate to the "Comment" view from -(void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex but just returned the tool bar will start working again.
So, can there be some kind of problem when I navigate to another view when then clickedButtonAtIndex is still on the call stack?
Update 2013-05-29:
If I add a method:
-(void)commentScreenYes{
[self vanigateToCommentScreen:YES];
}
And change the button code to:
-(void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex{
bool shouldAddNewComment = buttonIndex == [actionSheet cancelButtonIndex];
if(shouldAddNewComment){
[self performSelectorOnMainThread:#selector(commentScreenYes) withObject:nil waitUntilDone:NO];
return;
}
[self navigateToCommentScreen:shouldAddNewComment];
}
Then it will work for the cancel button, but not for the yes button. The cancel button will also make the toolbar work again after it have been "broken" by the yes button. If I pipe both buttons through this flow (by adding a new method) they will both work.
And still, if I run it at iOs 5.1 it will work no matter which flow I choose.
The – setToolbarItems method is a UIViewController method specific to each child view controller of your navigation controller. Assuming [self navigateToCommentScreen:shouldAddNewComment] pushes a new view controller onto the navigation stack you will have to set the toolbar items for the new view controller too as a separate call, regardless of what the action sheet is doing. For example,
-(void) navigateToCommentScreen:(bool) shouldAddComment{
CommentViewController* viewController = [[CommentViewController alloc] init];
[self.navigationController pushViewController:viewController animated:YES];
[viewController setToolbarItems:self.toolbarItems];
}
Otherwise the new view controller will have no toolbar items and the toolbar will be blank.
Edit
I created a navigation controller with a root view controller that has a toolbar with a UISegmented control. I pushed a new view controller onto the navigation stack via a UIActionSheet delegate method and then popped it back. The toolbar was exactly how it should be on the root view controller (the segmented control faded back in as expected). You must be doing something more complicated with your controller with regards to setting the toolbar items that is causing them to disappear. Without seeing your viewController's source methods it's hard to tell what the problem is. Make sure that you are not setting your toolbar items to nil or resetting them with an empty array somehow.