Reputation: 93
I have an UIAlertController
presented from a UIViewController
which is pushed in the UINavigationController
as the last UIViewController
. This UIAlertViewController
has a UITextField
in it.
My problem is that when I select the UITextField
the keyboard shows up, but the UIAlertViewController
stays centered, and partly hidden under the keyboard.
My code looks like this:
UIAlertController* alert = [UIAlertController alertControllerWithTitle:@"Enter Your PIN" message:@"Please enter your PIN!" preferredStyle:UIAlertControllerStyleAlert];
[alert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
[textField setSecureTextEntry:YES];
}];
UIAlertAction* okAction = [UIAlertAction actionWithTitle:@"Confirm"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * _Nonnull action) { }];
UIAlertAction* cancelAction = [UIAlertAction actionWithTitle:@"Cancel"
style:UIAlertActionStyleCancel
handler:^(UIAlertAction * _Nonnull action) {
}];
[alert addAction:okAction];
[alert addAction:cancelAction];
[self presentViewController:alert animated:YES completion:nil];
Upvotes: 9
Views: 2377
Reputation: 891
I concluded that this should a bug of iOS, so I decided to handle it by creating a normal UIView. Here is a .xib file that I created for mocking UIAlertcontoller with UITextField in it. Hope your time will be saved some at least.
https://www.dropbox.com/s/kg2nf9qcm4flhk0/AlertviewWithTextield.xib?dl=0
Upvotes: 0
Reputation: 2204
If I understood you, the problem is that alert is shown under keyboard. I think the problem is that the UIAlertController is shown on your viewController? but the keyboard is shown from rootViewController. Try this code to manage the problem
let alert = UIAlertController(title: "Title", message: NSLocalizedString("Message", comment: ""), preferredStyle: .alert)
let okAction = UIAlertAction(title: "OK", style: .default) { action in }
alert.addAction(okAction)
let rootViewController: UIViewController = UIApplication.shared.windows.last!.rootViewController!
rootViewController.present(alert, animated: true, completion: nil)
I wrote extension, because in some cases the code above could not work
extension UIApplication {
class func topViewController(base: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController? {
if let nav = base as? UINavigationController {
return topViewController(base: nav.visibleViewController)
}
if let tab = base as? UITabBarController {
let moreNavigationController = tab.moreNavigationController
if let top = moreNavigationController.topViewController, top.view.window != nil {
return topViewController(base: top)
} else if let selected = tab.selectedViewController {
return topViewController(base: selected)
}
}
if let presented = base?.presentedViewController {
return topViewController(base: presented)
}
return base
}
}
usage
let alert = UIAlertController(title: NSLocalizedString(title, comment: ""), message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
UIApplication.topViewController()?.present(alert, animated: true, completion: nil)
Upvotes: 1
Reputation: 577
You can add [self.view endEditing:YES];
to be sure the keyboard won't be show.
Upvotes: -1
Reputation: 4191
The keyboard is presented in a dedicated window. So is an alert, your viewcontrollers (see you application delegate's window
property) etc. It is the windowLevel
of UIWindow
instances that determine the z-order of the windows (i.e. how windows are presented above/below each other).
We have seen the same behaviour in our app where we use a custom window for custom alerts, which is always shown below the keyboard's window. It seems like window level behaviour has changed at some point during iOS 9/10 releases, so that the keyboard's window is always at the top of the window stack.
Since UIAlertController
's window is managed by the system, there is likely no way for 3rd party developers to modify this behaviour.
To inspect the window level of the alert's window, however, you can reference its view
's window
's windowLevel
property after you have presented it (eg in the presentation function's completion block). Perhaps even change it to a very large value in an attempt to force the alert's window level.
To track this issue you could duplicate this radar.
Upvotes: 0
Reputation: 531
You can manage your keyboard based on textField location.
// Call this method somewhere in your view controller in **viewdidload** method
- (void)registerForKeyboardNotifications
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWasShown:)
name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillBeHidden:)
name:UIKeyboardWillHideNotification object:nil];
}
//Add in your View Controller which Called when the UIKeyboardDidShowNotification is sent.
- (void)keyboardWasShown:(NSNotification*)aNotification
{
NSDictionary* info = [aNotification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0);
scrollView.contentInset = contentInsets;
scrollView.scrollIndicatorInsets = contentInsets;
// If active text field is hidden by keyboard, scroll it so it's visible
// Your app might not need or want this behavior.
CGRect aRect = self.view.frame;
aRect.size.height -= kbSize.height;
if (!CGRectContainsPoint(aRect, activeField.frame.origin) ) {
[self.scrollView scrollRectToVisible:activeField.frame animated:YES];
}
}
// Add in your View Controller which Called when the UIKeyboardWillHideNotification is sent
- (void)keyboardWillBeHidden:(NSNotification*)aNotification
{
UIEdgeInsets contentInsets = UIEdgeInsetsZero;
scrollView.contentInset = contentInsets;
scrollView.scrollIndicatorInsets = contentInsets;
}
Initialize UITextField * activeField;
Additional methods for tracking the active text field.
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
activeField = textField;
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
activeField = nil;
}
You can also check these concept in apple documents. https://developer.apple.com/library/content/documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/KeyboardManagement/KeyboardManagement.html
Upvotes: -3