Popover with ModalPresentationStyle is not centered in iOS 7 iPad
Asked Answered
B

6

21

I have a problem with iOS 7 that seems to be a bug or I just don't do something right. I have modalViewController that appears as a popover on iPad with ModalPresentationStyle. And it is not standard size, custom sized. Here is the code:

myViewController *myVC = [[myViewController alloc] init];
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:myVC];
[nav setModalPresentationStyle:UIModalPresentationFormSheet];
[nav setModalTransitionStyle: UIModalTransitionStyleFlipHorizontal];
[self presentViewController:nav animated:YES completion:nil];
nav.view.superview.bounds = CGRectMake(0, 0, 320, 465);

It's all working fine in iOS 6, but in iOS 7 it's not centered. But if I set ModalTransitionStyle to UIModalTransitionStyleCrossDissolve it works fine. But only in this mode. Maybe someone stumbled on this one too and know how to fix it? I'm not a big fan of dissolve effect. Thank you.

Barrera answered 22/9, 2013 at 4:45 Comment(0)
A
3

I have a method where the old custom modal presentation style fromsheet works with iOS <=7 although you might set custom height and width.

Keep in mind that this method might not work in any newer version in the future

- (void) hackModalSheetSize:(CGSize) aSize ofVC:(UIViewController *) aController;
{

    void (^formSheetBlock) (void) = ^{
        int preferredWidth = aSize.width;
        int preferredHeight = aSize.height;

        CGRect frame = CGRectMake((int) 1024/2 - preferredWidth/2,
                                  (int) 768/2 - preferredHeight/2,
                                  preferredWidth, preferredHeight);
        aController.view.superview.frame = frame;
        if([aController respondsToSelector:@selector(edgesForExtendedLayout)]) { //ios7
            aController.view.superview.backgroundColor = [UIColor clearColor];
        } else { // < ios7
            UIImageView *backgroundView = [aController.view.superview.subviews objectAtIndex:0];
            [backgroundView removeFromSuperview];
        }
    };

    //on ios < 7 the animation would be not as smooth as on the older versions so do it immediately
    if(![self respondsToSelector:@selector(edgesForExtendedLayout)]) {
        formSheetBlock();
        return;
    }

    double delayInSeconds = .05;
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);

    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
        formSheetBlock();
    });
}
Antiparticle answered 2/10, 2013 at 9:51 Comment(2)
I will accept this answer as this is working for me, at least for now. Though I ended up using MZFormSheetController - it's really a great ios 7 style solution, works for me in both ios versions. Though I'd really like to figure out in a future how to handle this with native ios kit, seems like a bug for now.Barrera
hey it's not working it's just show pop up and again going to full view in form sheet. any idea about it #30615575Armillda
R
39

I had the same problem. I have solved this by using another approach, found here.

What this solution proposes is to use the method (void)viewWillLayoutSubviews

So in case of @Manuel M. inside the GeneralSettingsViewController add the code below:

// GeneralSettingsViewController
- (void)viewWillLayoutSubviews{
    [super viewWillLayoutSubviews];
    self.view.superview.bounds = CGRectMake(0, 0, 497, 375);
}

And you won't need this code anymore:

self.generalSettingsVC.view.superview.frame = CGRectMake(0, 0, 497, 375);
self.generalSettingsVC.view.superview.center = self.view.center;

For @titicaca, you are using a UINavigationController I haven't test it with this Controller but you could try the same solution I mentioned, extending the UINavigationController and overwrite the viewWillLayoutSubviews method.

[EDIT]

For @titicaca I tried it in a new project and for me it worked. What I did was having a custom navigation view controller CustomNavigationController overriding the viewWillLayoutSubviewslike this:

- (void)viewWillLayoutSubviews{
    [super viewWillLayoutSubviews];
    self.view.superview.bounds = CGRectMake(0, 0, 330, 284);
}

Then, the view controller that presents the CustomNavigationController should execute a code similar to this:

UIViewController *myVC = [[UIViewController alloc] init];
[myVC.view setBackgroundColor:[UIColor redColor]];

CustomNavigationController *nav = [[CustomNavigationController alloc] initWithRootViewController:myVC];
[nav setModalPresentationStyle:UIModalPresentationFormSheet];
[nav setModalTransitionStyle: UIModalTransitionStyleFlipHorizontal];

[self presentViewController:nav animated:YES completion:nil];

You need to make sure though, that the dimensions of the self.view.superview.bounds = CGRectMake(0, 0, 330, 284); are even numbers otherwise the text inside gets fuzzy, if there is any

Retention answered 26/9, 2013 at 11:22 Comment(2)
I was wondering if someone could explain why a) the view.superview.bounds property has to get called in order to fix this issue, and b) how passing in (0,0) for the rect's origin causes it to center?Micrometry
I found viewWillAppear: is an even better place to put self.view.superview.bounds=xxx since it's called earlier than viewWillLayoutSubviewsChong
B
5

For me the issue was calling becomeFirstResponder on a text field in the viewDidAppear of the presented view controller. Appears to be a bug with that now. The solution was wrapping it in a simple dispatch_async:

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    dispatch_async(dispatch_get_main_queue(), ^{
        [self.userNameTextField becomeFirstResponder];
    });
}
Bushore answered 21/3, 2014 at 17:27 Comment(0)
F
4

It's the same for me... I don't know yet how to solve it. I'm currently working in that issue so anything I get I'll share it!

This is my code.

-(IBAction)showGeneralSettings:(id)sender{

self.generalSettingsVC = [[GeneralSettingsViewController alloc] initWithNibName:@"GeneralSettingsView" bundle:nil];

//Present the view controller as a modal with a custom size (important to do after presenting it)
self.generalSettingsVC.modalPresentationStyle = UIModalPresentationFormSheet;

[self presentViewController:self.generalSettingsVC animated:YES completion:nil];

self.generalSettingsVC.view.superview.frame = CGRectMake(0, 0, 497, 375);
self.generalSettingsVC.view.superview.center = self.view.center;

}

Futuristic answered 22/9, 2013 at 11:13 Comment(3)
Hey Manuel, did you find any solution?Barrera
Hey! the solution of @Tamara Bernad works really well. Try it. I cannot vote as good one because I have no reputation yet :(Futuristic
Unfortunately, it didn't work for me that well. It places the popover in the center, but it's not my size, it's just standard sized popover window with my stuff centered inside it as well..Barrera
A
3

I have a method where the old custom modal presentation style fromsheet works with iOS <=7 although you might set custom height and width.

Keep in mind that this method might not work in any newer version in the future

- (void) hackModalSheetSize:(CGSize) aSize ofVC:(UIViewController *) aController;
{

    void (^formSheetBlock) (void) = ^{
        int preferredWidth = aSize.width;
        int preferredHeight = aSize.height;

        CGRect frame = CGRectMake((int) 1024/2 - preferredWidth/2,
                                  (int) 768/2 - preferredHeight/2,
                                  preferredWidth, preferredHeight);
        aController.view.superview.frame = frame;
        if([aController respondsToSelector:@selector(edgesForExtendedLayout)]) { //ios7
            aController.view.superview.backgroundColor = [UIColor clearColor];
        } else { // < ios7
            UIImageView *backgroundView = [aController.view.superview.subviews objectAtIndex:0];
            [backgroundView removeFromSuperview];
        }
    };

    //on ios < 7 the animation would be not as smooth as on the older versions so do it immediately
    if(![self respondsToSelector:@selector(edgesForExtendedLayout)]) {
        formSheetBlock();
        return;
    }

    double delayInSeconds = .05;
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);

    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
        formSheetBlock();
    });
}
Antiparticle answered 2/10, 2013 at 9:51 Comment(2)
I will accept this answer as this is working for me, at least for now. Though I ended up using MZFormSheetController - it's really a great ios 7 style solution, works for me in both ios versions. Though I'd really like to figure out in a future how to handle this with native ios kit, seems like a bug for now.Barrera
hey it's not working it's just show pop up and again going to full view in form sheet. any idea about it #30615575Armillda
T
3

The solution above did not work for me. I used the following:

      UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:theViewController];
      navigationController.modalPresentationStyle=UIModalPresentationFormSheet;
      [self presentViewController:navigationController animated:YES completion:nil];
      if([[UIDevice currentDevice] userInterfaceIdiom] != UIUserInterfaceIdiomPhone){
            navigationController.view.superview.frame = CGRectMake(0, 0, 320, 544);
            navigationController.view.frame =CGRectMake(108, 0, 320, 544);
            navigationController.view.superview.backgroundColor=[UIColor clearColor];
      }
Torticollis answered 22/3, 2014 at 6:47 Comment(0)
T
1

I went about this slightly differently using a subclassed navigation controller and AutoLayout in Swift, to center the view in the superview at a set width and height. My particular case needed iPad and iOS7-8 support (there's some constants specific to my project in this code, but you get the idea) ...

class CenteredModalNavigationController: UINavigationController {

    // MARK:- Methods

    override func viewDidLoad() {
        super.viewDidLoad()
        preferredContentSize = CGSizeMake(320, 480) // iOS8 only
    }

    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)

        // iOS7 support for iPad custom sized form-sheet modal.
        if kDeviceiOS7 && kDeviceiPad {
            if view.superview == nil {
                Log.warning("Failed to find superview")
                return
            }
            view.superview!.setTranslatesAutoresizingMaskIntoConstraints(false)

            // Give the superview constraints to center the view inside it.
            var viewBindingsDict: NSMutableDictionary = NSMutableDictionary()
            viewBindingsDict.setValue(view, forKey: "view")
            viewBindingsDict.setValue(view.superview!, forKey: "superview")
            let centerXConstraints = NSLayoutConstraint.constraintsWithVisualFormat("V:[superview]-(<=1)-[view]",
                options: .AlignAllCenterX,
                metrics: nil,
                views: viewBindingsDict)
            view.superview!.addConstraints(centerXConstraints)

            let centerYConstraints = NSLayoutConstraint.constraintsWithVisualFormat("H:[superview]-(<=1)-[view]",
                options: .AlignAllCenterY,
                metrics: nil,
                views: viewBindingsDict)
            view.superview!.addConstraints(centerYConstraints)

            // Now give it a width/height and it should float in the middle of our superview.
            AutoLayoutHelper.addWidthConstraint(view,
                aSuperView: view.superview!,
                width: 320)
            AutoLayoutHelper.addHeightConstraint(view,
                aSuperView: view.superview!,
                height: 480)
        }
    }

    override func prefersStatusBarHidden() -> Bool {
        return true
    }

    override func disablesAutomaticKeyboardDismissal() -> Bool {
        // DEVNOTE: Because this is shown in a modal, this is required to stop keyboard dismiss issues.
        return true
    }

}

...

class AutoLayoutHelper: NSObject {

    class func addHeightConstraint(aChildView:UIView, aSuperView:UIView, height:CGFloat) {
        var constraint = NSLayoutConstraint(item: aChildView,
            attribute: NSLayoutAttribute.Height,
            relatedBy: NSLayoutRelation.Equal,
            toItem: nil,
            attribute: NSLayoutAttribute.NotAnAttribute,
            multiplier: 1.0,
            constant: height)
        aSuperView.addConstraint(constraint)
    }

    class func addWidthConstraint(aChildView:UIView, aSuperView:UIView, width:CGFloat) {
        var constraint = NSLayoutConstraint(item: aChildView,
            attribute: NSLayoutAttribute.Width,
            relatedBy: NSLayoutRelation.Equal,
            toItem: nil,
            attribute: NSLayoutAttribute.NotAnAttribute,
            multiplier: 1.0,
            constant: width)
        aSuperView.addConstraint(constraint)
    }

}
Townscape answered 25/12, 2014 at 14:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.