Search code examples
iosuiviewcontrolleruiinterfaceorientation

Orientation issue while presenting Modal ViewController


Current scenario:

Right now I am showing a UIViewController using a segue with the style Modal and presentation Sheet. This Modal gets its superview bounds change, in order to have the dimensions I want, like this:

- (void)viewWillLayoutSubviews
{
    [super viewWillLayoutSubviews];
    self.view.superview.bounds = WHBoundsRect;
}

The only allowed orientations are UIInterfaceOrientationLandscapeLeft and UIInterfaceOrientationLandscapeRight. Since the Modal has some TextFields and the keyboard would be over the Modal itself, I am changing its center so it moves a bit to the top.

The problem:

What I am noticing right now, is that I am unable to work with the Y coordinate. In order for it move vertically (remember it's on landscape) I need to work with the X. The problem is that when it's UIInterfaceOrientationLandscapeLeft I need to come with a negative X. And when it's UIInterfaceOrientationLandscapeRight I need to come with a positive X. So it seems that the X/Y Coordinate System is "glued" to the top left corner while in Portrait and when an orientation occurs, it's still there:

enter image description here

What I have done

So I have something like this:

UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
NSInteger newX = 0.0f;

if (orientation == UIInterfaceOrientationLandscapeLeft)
{
  // Logic for calculating the negative X.
}
else
{
  // Logic for calculating the positive X.
}

It works exactly like I want, but it seems a very fragile implementation. Am I missing something? Is this the expected behaviour?


Solution

  • Update

    I wrote a test project, you can find it here

    Here is the interesting part, the modal view controller subclass code.

    - (void)viewDidLoad
    {
        [super viewDidLoad];
    
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillAppear:) name:UIKeyboardWillShowNotification object:nil];
    }
    
    - (void)viewWillLayoutSubviews
    {
        [super viewWillLayoutSubviews];
        self.view.superview.bounds = WHBoundsRect;
    }
    
    - (void)keyboardWillAppear:(NSNotification*)notification
    {
        CGRect keyboardFrame = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
    
        // Calculate keyboard frame in superview coordinates.
        // Origin will be in the upper left corner of the superview, so keyboard will have a negative x origin.
        CGRect keyboardFrameInSuperviewCoordinates = [self.view.superview convertRect:keyboardFrame fromView:nil];
    
        NSLog(@"Superview bounds: %@", NSStringFromCGRect(self.view.superview.bounds));
        NSLog(@"Keyboard frame in screen coordinates: %@", NSStringFromCGRect(keyboardFrame));
        NSLog(@"Keyboard frame in superview coordinates: %@", NSStringFromCGRect(keyboardFrameInSuperviewCoordinates));
    
        // The keyboard and the superview lay directly in a window, so we should do the math on window coordinates
        // Converting  to the superview system takes account of rotation, so now y is relative to current orientation
        CGRect windowCoordinatesInSuperviewSystem = [self.view.superview convertRect:self.view.superview.window.bounds fromView:nil];
    
        NSLog(@"Window frame in superview coords: %@", NSStringFromCGRect(windowCoordinatesInSuperviewSystem));
    
    
        // We calculate for how much the superview is hidden by the keyboard
        CGFloat hiddenPartHeight = CGRectGetMaxY(self.view.superview.bounds)-CGRectGetMinY(keyboardFrameInSuperviewCoordinates);
        NSLog(@"Superview is hidden by keyboard for %.0f points", hiddenPartHeight);
    
        // Now we just have to shift the superview frame to the top by 'hiddenPartHeight'.
    
        // Last step: calculate new superview frame in super-superview coordinate system.
        // To do that, we convert the superview frame relative to its coordinate system to super-superview coordinate system.
        CGRect superviewNewFrameInItsOwnCoordinateSystem = CGRectOffset(self.view.superview.bounds, 0, -hiddenPartHeight);
        CGRect superviewAbsoluteFrame = [self.view.superview convertRect:superviewNewFrameInItsOwnCoordinateSystem toView:self.view.superview.superview];
    
        NSLog(@"Superview new frame in its own coords: %@", NSStringFromCGRect(superviewNewFrameInItsOwnCoordinateSystem));
        NSLog(@"Superview new frame: %@", NSStringFromCGRect(superviewAbsoluteFrame));
    
        self.view.superview.frame = superviewAbsoluteFrame;
    }