iPad自定义模态视图控制器的尺寸

56

我有几个特定大小的模态视图控制器。我试图避免使用自定义视图(创建全屏黑色半透明覆盖当前视图,将模态视图添加到该视图上,执行动画等)来呈现它,因为没有modalPresentationStyle适合我的控制器大小。

现在我正在使用UIModalPresentationPageSheet,但是我的视图高度较小,有一个丑陋的空白区域。

期望的呈现效果

 _______________
|    _______    |
|   |       |   |
|   |  MyVC |   |
|   |       |   |
|    -------    |
 --------------- 

实际呈现

 _______________
|   |       |   |
|   |  MyVC |   |
|   |       |   |
|   |-------|   |
|   | blank |   |
 ---------------    
如果我使用UIModalPresentationFormSheet,容器的宽度会变小。 我正在尝试弄清楚如何解决这个问题,但我不知道是否可能。有没有解决方案可以呈现比任何presentationStyles都小的模态VC?唯一的解决方案是安排一个“自定义模态视图控制器引擎”吗?弹出视图不符合我的设计要求:(

1
如果您点击答案旁边的灰色复选标记,接受正确的答案将是非常好的。 - Rajesh
13个回答

85

和其他用户一样,我也遇到了模态视图控制器源的问题。经过一些实验,我找到了一个适合我的解决方案:

- (void)viewWillLayoutSubviews{
    [super viewWillLayoutSubviews];   
    self.view.superview.bounds = CGRectMake(0, 0, <width>, <height>);
}

3
这对我有用。它可以正确地将重新调整大小的表单表格居中于iOS 6中。 - James Richards
2
同样适用于iOS 7。 - Renexandro
1
我想补充一点,如果您需要在显示视图时多次更改视图大小(例如展开/折叠),则需要设置一个类成员来保存CGRect,然后在willLayoutSubviews上添加if( CGRectIsEmpty( myRect ) ) { myRect = CGRectMake( 0, 0, <width>, <height> ); } self.view.superview.bounds = myRect;。每次更新superview时都会调用viewWillLayoutSubviews,因此如果您尝试在初始视图加载之外更频繁地调整其大小,则会发生奇怪的事情。 - Chris Ostmo
@Gon 它确实有效,我已经在不同的iOS 7应用程序中使用了这种简单的方法。为了使其工作,请确保在您的XIB文件或storyboard中没有设置任何其他大小,并检查您的presentViewController方法所在的代码,您没有为框架定义任何大小。简而言之,让viewWillLayoutSubviews方法成为设置模态视图框架大小的唯一方法,并记得将X和Y参数保留为0。 - Renexandro
3
如果您将至少一个可编辑的UITextField放在视图中,它将无法正常工作。当您尝试进入编辑模式时,框架会尝试显示键盘,然后系统就会陷入无限循环! - Magurizio
显示剩余6条评论

35

我通过以下方式获得了如下结果(链接文本):

self.modalPresentationStyle = UIModalPresentationFormSheet;

通过将其呈现为模态视图控制器来展示它。如果需要进一步帮助,请告诉我。


4
感谢您的回答,但是 FormSheet 不适合我的尺寸(我在问题中提到了)。 - emenegro
1
它能够工作,但是阴影与表单表格的原始框架相等。但是...这是一个不错的方法。谢谢,如果没有人回答,奖励将归您;) - emenegro
请查看下面的答案,它可以让您控制模态框的大小。 - Brenden
在你的代码示例中,self 是模态视图控制器还是呈现视图控制器?我尝试了两种方式都不起作用。 - abc123
在viewDidAppear方法中使用self.view.superview.frame来改变大小。 - Rambatino
显示剩余2条评论

24
所有这些答案都不能在iOS8 SDK上工作。
这个会起作用:
AboutViewController * _aboutViewController = [[AboutViewController alloc] init];
    _aboutViewController.modalPresentationStyle = UIModalPresentationFormSheet;
    if(IS_IOS8)
    {
        _aboutViewController.preferredContentSize = CGSizeMake(300, 300);
    }
    [self presentViewController:_aboutViewController animated:YES completion:nil];

在AboutViewController.m文件中。
- (void)viewWillLayoutSubviews{
    [super viewWillLayoutSubviews];

    if(!IS_IOS8)
    {
        self.view.superview.bounds = CGRectMake(0, 0, 300, 300);
    }
}

IS_IOS8

#define IS_IOS8 ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8)

在iOS 8中,您还可以使用UIPresentationController,这为您提供了更多的自定义选项。

感谢分享在iOS 8中的简易方法,我刚刚花了2个小时试图弄清楚如何做到这一点,而官方文档中现在没有任何相关内容。 - bontoJR
太好了!记得你也可以在Interface Builder中定义首选内容大小。 - MrAlek
preferredContentSize 对我来说很好用。但是我需要在 iOS 8 中改变我的位置。我应该使用哪个属性? - Vipin Vijay
@VipinVijay 我没有测试过这个方法,但也许你可以使用UIPresentationController和UIViewControllerTransitioningDelegate。请查看我的其他回答并尝试设置frameOfPresentedViewInContainerView参数:https://dev59.com/aoPba4cB1Zd3GeqPs3-6#25934574 - pawel_d

19

在自定义大小的模态框居中方面我遇到了问题,直到我弄明白 ViewController.view.superview.frame 最初的设定是什么。它是根据 UIModalPresentation 类型确定的,superview.center 甚至不需要(根据我的测试结果)。

另外,我使用 [UIScreen mainScreen].applicationFrame 来动态设置坐标,目前只支持横向方向。你必须进行条件检查以支持纵向和横向,通过翻转 X/Y 和 Height/Width 值来实现。

这是我在 iOS 6.0 上的第一个可行解决方案:

ViewController.modalPresentationStyle = UIModalPresentationFormSheet;
ViewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentViewController:ViewController animated:YES completion:nil];
ViewController.view.superview.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin;
ViewController.view.superview.frame = CGRectMake(
                                                                    // Calcuation based on landscape orientation (width=height) 
                                                                    ([UIScreen mainScreen].applicationFrame.size.height/2)-(320/2),// X
                                                                    ([UIScreen mainScreen].applicationFrame.size.width/2)-(320/2),// Y
                                                                    320,// Width
                                                                    320// Height
                                                                    );

然后我看了一下之前的答案,差点没跳起来。用最后一行替换可以缩短我的解决方案:

ViewController.view.superview.bounds = CGRectMake(0, 0, 320, 320);

编辑 最终解决方案和备注。


我通常使用同样的解决方案来调整模态视图的大小。但是我意识到它在iOS 6上不起作用。该视图始终会卡在父视图的顶部。有什么解决方法吗?谢谢 - Frade
我在iPad上测试了iOS 6,没有看到这种情况发生。你在测试什么?如果你测试一下,这个解决方案是否有效? - Brenden
我的问题是我无法更改父视图的框架起点。 它可以改变大小,但无法改变中心。只有在完成块上进行操作才有效。 - Frade
只是猜测,可能是因为在你改变它之后,它的控制器也在改变它,所以看起来好像你不能改变它。 - Brenden

18
这样做的最佳方式是更改表单视图内呈现视图所在的父视图的bounds属性。当以模态形式呈现视图时,呈现的视图嵌入到另一个视图中。
您应该将父视图的bounds属性设置为与视图的bounds相同的矩形。首先在您的类中添加一个私有的ivar:
@implementation MySpecialFormsheet {
    CGRect _realBounds;
} 

最好在viewWillAppear:中执行此操作。将以下代码添加到被模态呈现的视图控制器中:

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    self.view.superview.bounds = _realBounds;
}

你需要在 viewDidLoad 方法中缓存 _realBounds:

- (void)viewDidLoad {
    _realBounds = self.view.bounds;
    [super viewDidLoad];
}

1
在我的应用程序中,我想让新视图出现在左侧而不是中心。我该怎么做? - Fire Fist
3
不确定早期版本如何,但从 iOS 6.0 开始,视图不再居中。x 坐标始终从普通表单开始。使用 self.view.centerself.view.superview.center 似乎无法解决问题。 - memmons
@Answerbot 我不确定你想说什么。这种技术在我的iOS 6上运行良好。 - Rob Jones

7
即使要减小表单的高度和宽度,您也可以使用。
[self.view.superview setBounds:CGRectMake(0, 0, 450, 480)];

1
哇,我还是不敢相信这有多么容易!我原本计划构建自己的视图来模拟UIModalViewController效果。谢谢! - DZenBot

7
这是一个关于非自动布局视图的解决方案(没有尝试过自动布局),与iOS 7和iOS 8兼容。请注意,使用模态视图时,请确保以这种方式调用:
```swift presentViewController(modalViewController, animated: true, completion: nil) ``` <代码中的HTML标签已被省略>
CloudSettingsViewController *cloudController = [[CloudSettingsViewController alloc] initWithNibName:@"CloudSettingsView" bundle:nil];

CGRect originalBounds = cloudController.view.bounds;

cloudController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
cloudController.modalPresentationStyle = UIModalPresentationFormSheet;

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

在iOS 8中,可以在模态视图的viewDidLoad方法中设置preferredContentSize。
- (void)viewDidLoad {
    [super viewDidLoad];

    // backup of the original size (selected in interface builder)
    height = self.view.frame.size.height;
    width = self.view.frame.size.width;

    if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8) // versioni successive ios 8
        self.preferredContentSize = CGSizeMake(width, height); // this is necessary to get the correct size of the modal view
}

当iPad需要显示键盘时,这也适用于模态视图

在iOS 8的早期版本中,您应该在presentViewController调用之后设置边界:

[currentController presentViewController:controlPanelController animated:YES completion:nil];
if ([[[UIDevice currentDevice] systemVersion] floatValue] < 8) 
    cloudController.view.superview.bounds = originalBounds;//it's important to do this after 

preferredContentSize 对我来说运行良好。但是我需要在iOS 8中改变我的位置。我应该使用哪个属性? - Vipin Vijay
@VipinVijay:你解决了如何在iOS 8及以上版本中更改位置的问题吗? - UditS

5

上述方法需要针对iOS7进行微调:

// set desired bounds, then call -presentFromViewController:

@implementation PresentedViewController
{
    CGRect _presentationBounds;
}

- (void)presentFromViewController:(UIViewController *)viewController
{
    self.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
    self.modalPresentationStyle = UIModalPresentationFormSheet;

    _presentationBounds = self.view.bounds;

    [viewController presentViewController:self animated:YES completion:nil];
}

- (void)viewWillLayoutSubviews
{
    [super viewWillLayoutSubviews];

    if (!CGRectIsEmpty(_presentationBounds))
    {
        self.view.superview.bounds = _presentationBounds;
        _presentationBounds = CGRectZero;
    }
}

这段代码也适用于iOS6。


4

我也曾经遇到同样的问题,试了这里给出的很多答案都没有解决。最终,我想出了一个对我非常有效的解决方案。

以下是在呈现视图控制器时的代码:

    SomeViewController *sovcObj = [[SomeViewController alloc] initWithNibName:@"SyncOptionsViewController" bundle:nil];
someObj.modalPresentationStyle = UIModalPresentationFormSheet;
someObj.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentViewController:someObj animated:YES completion:nil];
someObj.view.superview.bounds = CGRectMake(0, 0, 400, 400); // whatever width and height you want

在xib中(如果您正在使用xib),在“模拟指标”部分选择“Freeform”大小。
此外,您需要在您的呈现的视图控制器中编写以下代码(即此示例中的SomeViewController)。
- (void)viewWillLayoutSubviews{
[super viewWillLayoutSubviews];
self.view.superview.bounds = CGRectMake(0, 0, 400, 400); // whatever width and height you want

这个代码同样也适用于iOS 7


1
我真希望我能给这个点赞两次。我尝试了所有的方法(花了我整整三个小时只为此事),直到最后终于成功了。非常感谢你。 - LMVogel

3

一种解决方案是使用UIModalPresentationPageSheet来呈现一个页面,并立即调整模态视图的大小。这在另一个问题的答案中有详细说明。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接