I'm coding some iPad application, and a strange effect has started to appear.
I use a navigation bar so I can navigate freely through my views. Anyway, in one of these innerviews, I offer the possibility of checking if the user really wants to go back to the main view via an alert view (Just a Yes/no pair of buttons).
Once the user confirms he wants to go back, then I "manually" pop the view, and I navigate to the main view, where the keyboard hides.
BUT, the thing is virtual keyboard acts weirdly. Inside the "inner view", the keyboard is visible 90% of the time (It's normal, there are many UITextFields around). What do I do to hide it whenever I want it to become hidden? I put something like this in the "main viewWillAppear":
[self.view endEditing:YES];
Apparently it hides the keyboard, and when I do:
[[self navigationController] popToRootViewControllerAnimated:YES];
The iPad navigates back, and then the keyboard disappears. No big deal (the first time).
But then I decide to reenter the same view, so the keyboard appears, I do some fancy stuff. Then I decide to abort (with my button), I show the same alert view again, and I confirm I want to go back to the main view (just as before). The code is the same, but then, in the main view, the:
[self.view endEditing:YES];
line, does nothing, and what is worst, I've lost the ability to close the keyboard by code ANYWHERE inside my application, for the whole session.
Why do I know the UIAlertView has something to do? Because if I change the code inside my "Abort button" from this:
alertViewQuestionType=ALERT_ABANDON_TEST;
UIAlertView *alert = [[[UIAlertView alloc] initWithTitle:#"" message:NSLocalizedStringFromTable (#"STR_ABANDON_TEST_WARNING", #"ui_strings", #"") delegate:self cancelButtonTitle:NSLocalizedStringFromTable (#"STR_CANCEL", #"ui_strings", #"") otherButtonTitles:nil] autorelease];
[alert addButtonWithTitle:NSLocalizedStringFromTable (#"STR_ABANDON", #"ui_strings", #"")];
[alert show];
to this:
[[self navigationController] popToRootViewControllerAnimated:YES];
(that is, no confirmation for the user, the user just navigates back, whenever the abort button is pressed), then when the main view reaches the "viewWillAppear" function, the hide keyboard code, works. Not only THAT time, but ALL the time until the end of my app session.
I don't know if my question is 100% clear, but I'll be more than glad to add some extra pieces of code here if someone believes he can shed some light to this mistery...
Greetings!
I've finally solved my problem. I'm more than sure that I could have done better, but for now, even I still don't know "what causes the aforementioned behaviour", I'm more than happy.
The thing is that if I "popped back" a view, FROM inside the UIAlertView delegate code, iOS thinks it's something I shouldn't do, and its internal keyboard management code "becomes out of control". It's as if I'm popping back "too soon", or without having let iOS close whatever keyboard data structures it needed to close.
So a coworker (aleixventa) gave me a hint: "Why don't you delay a bit the 'popback' code, wrapping it inside a "NSTimer structure"?. And that's precisely what I did. My 'pop back' manual code, is now this:
NSTimer* testTimer = [NSTimer scheduledTimerWithTimeInterval:.05 target:self selector:#selector(popViewPorTimer) userInfo:nil repeats: NO];
Having a small function for my popViewPorTimer selector:
-(void) popViewPorTimer
{
[[self navigationController] popViewControllerAnimated:YES];
}
And now, no matter how many times the keyboard is dismissed via "Navigation pop back", it always becomes hidden. YAY!!
For extra bonus, if someone knows why all this happens, it will be a more than welcome piece of info.
I love when I "solve my questions" by myself :)
Well, I must have been completely misled, because I've removed all traces of [self.view endEditing:YES] and/or resignFirstResponder in ALL my .m files and I've discovered 2 things:
1.- They weren't doing ANYTHING. Keyboard auto disappears when needed.
2.- The bug I originally described, is still there.
So, as far as I know, when I just "press back" in the actual UINavigationBar "Back" button (in any inner view), the view, pops back, and if keyboard was present, it disappears. So far so good.
Then some other times, I need to simulate a "Back" press button. I do it with:
[[self navigationController] popViewControllerAnimated:YES];
It works flawlessly, that is, it just pops back, and if keyboard was present, it disappears.
BUUUT, if I write this "manual back" action INSIDE some UIAlertView Delegate I also need (where I deal with two Accept/Cancel buttons), this "manual back action" also pops back to the "parent" view (always, that's OK), but it hides the keyboard ONLY the first time it's called. Succesive calls to "manual back" via this accept/cancel UIAlertDelegate function, render the "keyboard hiding engine" completely unusable for the rest of the session.
I'm plain lost.
Related
I'm looking at a friend's code and not sure how this works. There's a singleton that talks to a web service to download data. The WebServiceObject is a subclass of NSObject. The download comes from a click on the home page which is a UIViewController.
In the WebServiceObject, when something goes wrong, he does:
UIAlertView *alert = [[UIAlertView alloc] init...];
[alert show];
The alert shows. How does that alert show though since the code that executes it is a non-view object? The reason I'm asking is because I want to remove that alert when the network comes back on. Since the network observing is happening in the WebServiceObject, I'm not sure how to access the UIAlertView object. I thought in other code I've done, that I would do something like
[self.view viewWithTag:NETWORK_ALERT_TAG];
or something to that affect. Is this because the view code is in the model code and instead I should change his code to post a notification to the view code to display the alert instead and grab it that way? Thanks a bunch!
UIAlertView’s -show method creates its own window, overlaid on top of the app’s window, in which to display itself; as such, it isn’t part of the app’s view hierarchy. To hide it, you’ll need a reference to the alert view itself, maybe exposed as a property on your WebServiceObject.
It sounds like you want to have the UIAlertView without any buttons, I found this nice tutorial that presents a "Please Wait Alert"
http://mobiledevelopertips.com/user-interface/uialertview-without-buttons-please-wait-dialog.html
I think it will help you achieve what you want, if you don't want the user to be able to dismiss the alert
I think this scenario should be weird one but i am stuck up with this.
I am having a view lets say View1, which will show one or more alertviews.Alerts are stacked one above the other if they are untouched.
The problem comes here.
If i click the top most alert ,it should take me to a complete new view lets say View2. And it does. Now i am getting the remaining alerts still shown in View2. I do not want this to happen . How can i auto dismiss the pending alerts created from View1 which are currently being shown in View2 ?
Any help or any idea is really appreciated..
Thanks
Try this,
UIAlertView *autoAlertView = [[UIAlertView alloc] initWithTitle:#"Auto-dismissed Alert" message:#"This alert will be dismissed in 5 seconds." delegate:self cancelButtonTitle:#"Cancel" otherButtonTitles:nil];
[self performSelector:#selector(dismissAlert:) withObject:autoAlertView afterDelay:5];
[autoAlertView show];
[autoAlertView release];
-(void)dismissAlert:(UIAlertView *)alert {
[alert dismissWithClickedButtonIndex:0 animated:YES];
}
Not quite the answer to your question that you wanted, but...
Why do you have so many alerts? It sounds like you might be overusing them. Apple is quite clear in its Human Interface Guidelines about how you should use UIAlerts:
Avoid creating unnecessary alerts.
These alerts are usually unnecessary
if they:
Merely increase the visibility of some information, especially
information that is related to the
standard functioning of your
application.
Instead, you should design an
eye-catching way to display the
information that harmonizes with your
app’s style.
Update users on tasks that are progressing normally.
Instead, consider using a progress
view or an activity indicator to
provide progress-related feedback to
users (these methods of feedback are
described in “Progress View” and
“Activity Indicator”).
Ask for confirmation of user-initiated actions.
To get confirmation for an action the
user initiated, even a potentially
risky action such as deleting a
contact, you should use an action
sheet.
Inform users of errors or problems about which they can do nothing.
Although it might be necessary to use
an alert to tell users about a
critical problem they can’t fix, it’s
better to integrate such information
into the UI, if possible. For example,
instead of telling users every time a
server connection fails, display the
time of the last successful
connection.
If you're overusing alerts: don't. Then your original question may become moot.
You can dismiss the other UIAlertViews programmatically using:
- (void)dismissWithClickedButtonIndex:(NSInteger)buttonIndex animated:(BOOL)animated
You'll need to store a reference to them at creation to do this.
Thanks, guys! I wrote my own substitute for UIAlertView. I'm putting halftransparent UIView with frame (0, 0, 320, 480) to avoid user interaction while my custom alert still says "Please wait", and above this background view I'm putting this custom alert which is just basic UIView with UIActivityIndicatorView on it
P.S. Don't judge me for my english, i'm russian
I have a question about how I close and release the custom designed UIAlertView. In my UIAlertView, i'm using the dismissWithClickedButtonIndex function; however, it only closes to the background, it doesn't close in real manner.
Does anyone have a solution for that?
How does it close to the background? I assume you are refering to the pointer of the UIAlertView is still valid after dismissing? In that case you need to [myAlertView release]; to "make it go away completely".
I have been studying how to display a modal view with UIAlertView for a few hours and I understood that showing it does not "block" the code (the modal window is displayed and the program keeps running - we must use delegate to catch the selected actions on this modal window). Then I studied several examples and noticed that every example always release the modal window just after showing it. How can this work properly since the view will be released instantly as the code does not stop ?
Here is the example (there are many others on Google):
[[UIAlertView alloc] initWithTitle:#"Title" message:#"Message..." delegate:NULL cancelButtonTitle:#"OK" otherButtonTitles:NULL];
[alert showModal];
[alert release];
Thanks for your help,
Apple 92
I'm not sure where you're getting -showModal from (the usual method is just -show), but that act adds the alert to the view hierarchy. When a view is added as a subview of another view (I believe in this case it's a system-level view that is being added to) it's retained automatically, so you don't have to.
The alloc method will return you an instance that has a retain count of 1.
The showModal method probably retains the alert view so it remains on screen (and retained) until a button is tapped. It makes sense to me, since you are presenting it as a modal window, so it doesn't have a "parent", that is responsible of releasing it.
I have a UINavigationController with a root view controller and then I push a UIViewController onto the navigation controller's stack. When the user taps the backBarButtonItem I'd like to be able to have an alert view pop up if there are certain conditions met and cancel the pop of the view controller. For example, the user can make certain selections but some combination of them may be invalid so I want to notify them to make changes.
I know that I can prevent the user from making an invalid combination or have an alert view pop up when the invalid combination is selected but I'd rather not do that. The user may be changing selections and may be aware that a certain combination is invalid but I'd rather let them select something that makes the combination invalid then go change something else (and notify them if they haven't made changes before trying to go to the previous screen). For example, if I prevent them from selecting something that makes an invalid combination then they may have to scroll up on a screen, change something, then scroll back down instead of making a selection then scrolling up and changing something.
Using viewWillDisappear: doesn't work because, although I can produce an alert view, I cannot figure out a way to prevent the pop from occurring. The alert view displays but the view controller still pops and they are back to the root view controller (with the alert view displaying).
Is there a way to prevent the pop from occurring? If not, is this something worth filing a bug report about or is this unnecessary and/or esoteric?
You can replace the back button with your own that calls the method you want in loadView
cancelButton = [[UIBarButtonItem alloc] initWithTitle:#"Cancel"
style:UIBarButtonItemStyleBordered
target:self
action:#selector(onCancelButtonSelected:)];
self.navigationItem.leftBarButtonItem = cancelButton;
then later
-(void)onCancelButtonSelected:(id)sender {
if (!hasSavedProduct)
{
cancelAlert = [[UIAlertView alloc] initWithTitle:#"Product not saved"
message:#"Exit without saving?"
delegate:self
cancelButtonTitle:#"Exit"
otherButtonTitles:#"Save",
nil];
[cancelAlert show];
[cancelAlert release];
}
then let them go
- (void)alertView:(UIAlertView *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
if ([cancelAlert isEqual:actionSheet])
{
if (buttonIndex == 0)
{
NSLog(#"Fine. Exiting without saving");
[self.navigationController popViewControllerAnimated:YES];
}
else
{
NSLog(#"Save here");
}
}
This sounds like something more appropriate for a Modal View Controller than it is for a View Controller on a Navigation stack.
If you're married to doing it on the stack though, it'd be nice if you could do this with UINavigationControllerDelegate, but you can't.
Is it possible to set the Back button to disabled, until the entries are valid? Perhaps when the user tries to enter something, but it's invalid, near the top of the View you have a Label animate into place with red text that tells the user they need to fix it. Meanwhile the back button is disabled and it's re-enabled after they make corrections.
Or get really creative with how your UI controls work to ensure that the user can never enter bad data.
Let them go back, just don't save anything unless it's completely valid. That's typically the approach Apple takes.