Presenting a view from a UIAlertController
moves the alert to a buggy position at the top-left corner of the screen. iOS 8.1, device and simulator.
We h
EDIT: Tested in 2020, Xcode 11.2, iOS 13
If anyone still looking for a better answer to this, then here is my solution.
Use updateConstraints
method to readjust the constraints.
your_alert_controller_obj.updateConstraints()
var kbHeight: CGFloat = 0
override func keyboardWillShow(_ notification: Notification) {
if let userInfo = notification.userInfo {
if let keyboardSize = (userInfo[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
kbHeight = keyboardSize.height
self.animateTextField(up: true)
}
}
}
override func keyboardWillHide(_ notification: Notification) {
self.animateTextField(up: false)
}
func animateTextField(up: Bool) {
let movement = (up ? -kbHeight : kbHeight)
UIView.animate(withDuration: 0.3, animations: {
self.view.frame = CGRect.offsetBy(self.view.frame)(dx: 0, dy: movement)
})
}
I encountered a situation where sometimes a modal view would present itself on top of a an alert (silly situation, I know), and the UIAlertController could appear in the top left (like the 2nd screenshot of the original question), and I found a one-liner solution that seems to work. For the controller that's about to be presented on the UIAlertController, change its modal presentation style like so:
viewControllerToBePresented.modalPresentationStyle = .OverFullScreen
This should be done just before you call presentViewController(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion completion: (() -> Void)?)
A quick fix is to always present the View Controller on top of a new UIWindow:
UIWindow *window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
window.rootViewController = [[UIViewController alloc] init];
window.windowLevel = UIWindowLevelNormal;
[window makeKeyAndVisible];
[window.rootViewController presentViewController: viewController
animated:YES
completion:nil];
I think that you only should to categorize UIAlertController like this:
@implementation UIAlertController(UIAlertControllerExtended)
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
if (self.preferredStyle == UIAlertControllerStyleAlert) {
__weak UIAlertController *pSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
CGRect screenRect = [[UIScreen mainScreen] bounds];
CGFloat screenWidth = screenRect.size.width;
CGFloat screenHeight = screenRect.size.height;
[pSelf.view setCenter:CGPointMake(screenWidth / 2.0, screenHeight / 2.0)];
[pSelf.view setNeedsDisplay];
});
}
}
@end
I was having this issue as well. If I presented a view controller while a UIAlertController was presented, the alert would go to the top left.
My fix is to refresh the center of the UIAlertController's view in viewDidLayoutSubviews; achieved by subclassing UIAlertController.
class MyBetterAlertController : UIAlertController {
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let screenBounds = UIScreen.mainScreen().bounds
if (preferredStyle == .ActionSheet) {
self.view.center = CGPointMake(screenBounds.size.width*0.5, screenBounds.size.height - (self.view.frame.size.height*0.5) - 8)
} else {
self.view.center = CGPointMake(screenBounds.size.width*0.5, screenBounds.size.height*0.5)
}
}
}