UIAlertController is moved to buggy position at top of screen when it calls `presentViewController:`

前端 未结 12 1366
心在旅途
心在旅途 2020-12-08 06:36

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

12条回答
  •  独厮守ぢ
    2020-12-08 07:29

    In addition to Carl Lindberg's answer There are two cases that also should be taken into account:

    1. Device rotating
    2. Keyboard height when there is a text field inside alert

    So, the full answer that worked for me:

    // fix for rotation
    
    -(void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id)coordinator
    {
        [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
        [coordinator animateAlongsideTransition:^(id context) {
        } completion:^(id context) {
            [self.view setNeedsLayout];
        }];
    }
    
    // fix for keyboard
    
    - (void)viewWillAppear:(BOOL)animated
    {
        [super viewWillAppear:animated];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
    }
    
    -(void)viewWillDisappear:(BOOL)animated
    {
        [super viewWillDisappear:animated];
        [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
        [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
    }
    
    - (void)dealloc
    {
        [[NSNotificationCenter defaultCenter] removeObserver:self];
    }
    
    - (void)keyboardWillShow:(NSNotification *)notification
    {
        NSDictionary *keyboardUserInfo = [notification userInfo];
        CGSize keyboardSize = [[keyboardUserInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
        self.keyboardHeight = keyboardSize.height;
        [self.view setNeedsLayout];
    }
    
    - (void)keyboardWillHide:(NSNotification *)notification
    {
        self.keyboardHeight = 0;
        [self.view setNeedsLayout];
    }
    
    // position layout fix
    
    -(void)viewDidLayoutSubviews
    {
        [super viewDidLayoutSubviews];
        [self fixAlertPosition];
    }
    
    -(void)fixAlertPosition
    {
        if (self.preferredStyle == UIAlertControllerStyleAlert && self.view.window)
        {
            CGRect myRect = self.view.bounds;
            CGRect windowRect = [self.view convertRect:myRect toView:nil];
            if (!CGRectContainsRect(self.view.window.bounds, windowRect) || CGPointEqualToPoint(windowRect.origin, CGPointZero))
            {
                CGRect myFrame = self.view.frame;
                CGRect superBounds = self.view.superview.bounds;
                myFrame.origin.x = CGRectGetMidX(superBounds) - myFrame.size.width / 2;
                myFrame.origin.y = (superBounds.size.height - myFrame.size.height - self.keyboardHeight) / 2;
                self.view.frame = myFrame;
            }
        }
        else if (self.preferredStyle == UIAlertControllerStyleActionSheet && self.traitCollection.userInterfaceIdiom == UIUserInterfaceIdiomPhone && self.view.window)
        {
            CGRect myRect = self.view.bounds;
            CGRect windowRect = [self.view convertRect:myRect toView:nil];
            if (!CGRectContainsRect(self.view.window.bounds, windowRect) || CGPointEqualToPoint(windowRect.origin, CGPointZero))
            {
                UIScreen *screen = self.view.window.screen;
                CGFloat borderPadding = ((screen.nativeBounds.size.width / screen.nativeScale) - myRect.size.width) / 2.0f;
                CGRect myFrame = self.view.frame;
                CGRect superBounds = self.view.superview.bounds;
                myFrame.origin.x = CGRectGetMidX(superBounds) - myFrame.size.width / 2;
                myFrame.origin.y = superBounds.size.height - myFrame.size.height - borderPadding;
                self.view.frame = myFrame;
            }
        }
    }
    

    Also, if using category, then you need to store keyboard height somehow, like this:

    @interface UIAlertController (Extended)
    
    @property (nonatomic) CGFloat keyboardHeight;
    
    @end
    
    @implementation UIAlertController (Extended)
    
    static char keyKeyboardHeight;
    
    - (void) setKeyboardHeight:(CGFloat)height {
        objc_setAssociatedObject (self,&keyKeyboardHeight,@(height),OBJC_ASSOCIATION_RETAIN);
    }
    
    -(CGFloat)keyboardHeight {
        NSNumber *value = (id)objc_getAssociatedObject(self, &keyKeyboardHeight);
        return value.floatValue;
    }
    
    @end
    

提交回复
热议问题