9

My iPad application opens modal view controller with 'Page' presentation style. As you know 'Page' presentation style doesn't cover status bar of presenting view controller to indicate page presentation.

Initial view controller

Page modal view controller

From the modal view controller the app opens UIImagePickerController to make photo. UIImagePickerController has 'Full screen' presentation style. After dismissing image picker presenting modal view controller become 20px taller and overlaps status bar of the initial view controller.

I tried to replace UIImagePickerController with simple UINavigationController and it breaks my modal view controller too.

There are screen shots: Full screen view controller

Broken modal view controller

They only way to restore size of 'Page' view controller is changing height of viewController.view.superview.superview.superview.superview frame after returning to 'Page' view controller. But it's really weird.

Is there another way to fix 'Page' modal view controller presentation after dismissing nested modal view controller?

UPDATE: I used such weird code to solve my problem:

#define STATUS_BAR_HEIGHT 20
#define IPAD_PORTRAIT_HEIGHT 1004
#define IPAD_LANDSCAPE_HEIGHT 748
UIView *superview = nil;

// In case of this view controller included in navigationController we have to use superview of navigation's controller view
if (self.navigationController)
    superview = self.navigationController.view.superview;
else
    superview = self.view.superview;

CGRect r = superview.frame;

// Sometimes we have to fix height + origin, sometimes only height (becase view has bottom magnifying)
// In landscape orientation we have to fix 'width' instead of 'height', because that view controller always works in 'portrait' mode
if (self.interfaceOrientation == UIInterfaceOrientationPortrait && r.size.height > IPAD_PORTRAIT_HEIGHT) {
    r.origin.y = STATUS_BAR_HEIGHT;
    r.size.height = IPAD_PORTRAIT_HEIGHT;
}
else if (self.interfaceOrientation == UIInterfaceOrientationMaskPortraitUpsideDown && r.size.height > IPAD_PORTRAIT_HEIGHT) {
    r.size.height = IPAD_PORTRAIT_HEIGHT;
}
else if (self.interfaceOrientation == UIInterfaceOrientationLandscapeLeft && r.size.width > IPAD_LANDSCAPE_HEIGHT) {
    r.size.width = IPAD_LANDSCAPE_HEIGHT;
    r.origin.x = STATUS_BAR_HEIGHT;
}
else if (self.interfaceOrientation == UIInterfaceOrientationLandscapeRight && r.size.width > IPAD_LANDSCAPE_HEIGHT) {
    r.size.width = IPAD_LANDSCAPE_HEIGHT;
}

superview.frame = r;

I don't believe that there is no more elegant solution. Any ideas how to improve it?

UPDATE2: I've just opened a bug. You can follow it there: rdar://15949644

UPDATE3: There is my sample project: link

Vitaly S.
  • 2,389
  • 26
  • 40
  • This is a known Apple bug. It has not been fixed in iOS7.1. Make sure to open a bug report. – Léo Natan Jan 28 '14 at 21:31
  • @LeoNatan, have you ever seen this problem somewhere else? I want to collect a bit more details before opening a bug report. – Vitaly S. Jan 29 '14 at 22:29
  • Yes, in our own app. We have a modal page sheet view controller presenting a camera view controller in fullscreen, and we experience the same visual anomaly after dismissing. We deemed it minor problem, so we only opened a bug report and didn't look for a workaround. I could take a look during the weekend and see if I can come up with a more elegant workaround, perhaps somewhere in the layouting system, but I can make no promises. – Léo Natan Jan 29 '14 at 23:09
  • @LeoNatan, anyway thanks for the info. I'll update my question after opening a bug report. – Vitaly S. Jan 29 '14 at 23:19
  • @LeoNatan, I've just opened a bug and updated the post – Vitaly S. Jan 30 '14 at 20:48
  • if possible link your sample project – codercat Feb 01 '14 at 11:30
  • @iDev, check the third update – Vitaly S. Feb 01 '14 at 14:22
  • 1
    This reproduces very easily, and your workaround is good. – Léo Natan Feb 01 '14 at 14:24
  • @VitalyS. check my answer and let me know if it works as you expect. – Dimitris Bouzikas Feb 04 '14 at 13:46
  • I think in this line: 'r.origin.x = STATUS_BAR_HEIGHT;' you meant r.origin.y. Does seem to work, stupid bug... Edit: Also, setting r.size.width to IPAD_LANDSCAPE_HEIGHT doesn't make sense, width shouldn't need to be changed, just r.size.height. – jeffff Apr 01 '14 at 01:53
  • @pssdbt, no I'm right. That superview always works in the same orientation, so if your view is in Landscape orientation, superview anyway is in portrait, so you have to change its width, not height. You can download sample project and play with it. – Vitaly S. Apr 01 '14 at 09:22
  • @VitalyS. Ah, in my app it shifted the modal to 20px off the left edge instead of keeping it centered, like it sounded like it would. I changed it and it doesn't anymore. Weird problem. – jeffff Apr 01 '14 at 09:24

5 Answers5

2

There is no good solution, it's an Apple bug, and until it is fixed, you have to work around it. It has not been fixed in iOS 7.1. I worked on a solution to this and realized I was implementing the same solution as well. It's ugly, but it works.

A word on this design. My guess why Apple overlooked this problem is because presenting a view controller in fullscreen is not something Apple would do. This is not an excuse of course, and sometimes there is no other option but to present fullscreen (we had to open a camera view, which must be opened in fullscreen, for example). Perhaps you can change your design to accommodate Apple's bugs.

Léo Natan
  • 56,823
  • 9
  • 150
  • 195
  • Actually I faced with this problem, because I have to present `UIImagePickerController`, so I don't have other options. Thanks – Vitaly S. Feb 04 '14 at 19:21
  • :-) We deemed this problem low priority and will basically leave it to Apple to fix their problem. – Léo Natan Feb 04 '14 at 19:21
1

"Ensure that the ViewController that is presenting the modal view is in a NavigationController and this weirdness should stop. " My initial answer - wrong

Updated This does indeed sound like a bug, although from a user experience point of view one you shouldn't really hit. It seems a little wrong to present a view controller that is in the 'Page' presentation style and then to present another over it in full screen mode.

IMO that's just bad design from the outset so the fact that it doesn't act as you'd expect probably is because whoever set it up didn't anticipate someone using it like that.

I would, pretty much as my initial answer stated albeit briefly, embed your modally presented view controller in a navigation controller and push the UIImagPickerViewController in to that or add it's view animated to yours as if it then appears to be presented like another page styled modal view. If that's you desired affect.

None of that sounds perfect, a more perfect solution would be to look at the flow of you app and perhaps reevaluate how things are presented.

AppHandwerker
  • 1,758
  • 12
  • 22
-1

I think it is a problem of iOS7 where layouts of Views has been slightly changed. I had similar problem with a pop modal custom View where I noticed the same behavior. I solved by adding this line in the viewDidLoad

self.edgesForExtendedLayout = UIRectEdgeNone;

Maybe it could be useful for some hint to your problem.

gdm
  • 7,647
  • 3
  • 41
  • 71
-1

There is definitely a problem with UIImagePicker (or more broadly, UINavigationController) not obeying any status bar settings in the application.

This has been discussed and solved by reapply your status bar settings in the UINavigationController delegate (see UIImagePickerController breaks status bar appearance).

In your case, you might want to call self.edgesForExtendedLayout = UIRectEdgeNone; or try ressetting some status bar properties in the delegate callback

- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated;
Community
  • 1
  • 1
Jack
  • 16,677
  • 8
  • 47
  • 51
  • This is incorrect. The second image is how a modal page sheet is supposed to open. – Léo Natan Jan 28 '14 at 21:32
  • @LeoNatan This is a full screen modal page though. in iOS 7 ALL views should go behind the status bar. See https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/TransitionGuide/Bars.html – Jack Jan 28 '14 at 21:33
  • 1
    Have you even read the question? The problem occurs only after the full screen modal view controller is dismissed. The page sheet is layout incorrectly. – Léo Natan Jan 28 '14 at 21:34
  • 1
    @LeoNatan My apologies I did indeed misread the question, I will edit my answer – Jack Jan 28 '14 at 21:36
  • @JackWu, this problem exists with all modal view controllers displayed in 'Page' mode. I was able to reproduce it without UIImagePickerController or UINavigationController – Vitaly S. Jan 29 '14 at 22:31
-3

Looks like it's an iOS bug and for now it can be solved with such workaround (it's a method of 'Page' modal view controller)

#define STATUS_BAR_HEIGHT 20
#define IPAD_PORTRAIT_HEIGHT 1004
#define IPAD_LANDSCAPE_HEIGHT 748

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear: animated];

    // In case of iOS7 we need this weird code, because after displaying full screen view controller from 'Page' modal view controller our 'Page' controller
    // incorrectly shifts 20px up
    //
    // I haven't found best solution for now
    // There is my question on SO: http://stackoverflow.com/questions/21146801/ipad-ios7-page-modal-view-controller-strange-behaviour-after-presenting-full
    if (!isIOS6()) {
        [self setNeedsStatusBarAppearanceUpdate];

        UIView *superview = nil;

        // In case of this view controller included in navigationController we have to use superview of navigation's controller view
        if (self.navigationController)
            superview = self.navigationController.view.superview;
        else
            superview = self.view.superview;

        CGRect r = superview.frame;

        // Sometimes we have to fix height + origin, sometimes only height (becase view has bottom magnifying)
        // In landscape orientation we have to fix 'width' instead of 'height', because that view controller always works in 'portrait' mode
        if (self.interfaceOrientation == UIInterfaceOrientationPortrait && r.size.height > IPAD_PORTRAIT_HEIGHT) {
            r.origin.y = STATUS_BAR_HEIGHT;
            r.size.height = IPAD_PORTRAIT_HEIGHT;
        }
        else if (self.interfaceOrientation == UIInterfaceOrientationMaskPortraitUpsideDown && r.size.height > IPAD_PORTRAIT_HEIGHT) {
            r.size.height = IPAD_PORTRAIT_HEIGHT;
        }
        else if (self.interfaceOrientation == UIInterfaceOrientationLandscapeLeft && r.size.width > IPAD_LANDSCAPE_HEIGHT) {
            r.size.width = IPAD_LANDSCAPE_HEIGHT;
            r.origin.x = STATUS_BAR_HEIGHT;
        }
        else if (self.interfaceOrientation == UIInterfaceOrientationLandscapeRight && r.size.width > IPAD_LANDSCAPE_HEIGHT) {
            r.size.width = IPAD_LANDSCAPE_HEIGHT;
        }

        superview.frame = r;
    }
}
Vitaly S.
  • 2,389
  • 26
  • 40
  • Guys who voted down this post, any suggestions how to improve this answer? I haven't found another working solution yet. – Vitaly S. Feb 04 '14 at 10:12