在模态页表上呈现模态表单表格

4
在iPad上,我使用modalPresentationStyle UIModalPresentationPageSheet显示一个模态视图控制器。这个视图控制器使用modalPresentationStyle UIModalPresentationFormSheet呈现另一个模态视图控制器。
所以,用户可以看到背景视图控制器、页面表和窗体表,因为窗体表比页面表小。页面表的呈现让背景变暗,这样就无法与其交互。然而,在iOS 5上,窗体表不会使页面表变暗,因此用户仍然可以在其下面与页面表交互。但是我希望页面表也变暗,这样用户在再次与页面表交互之前必须关闭模态窗体表。
在iOS 4上,这是默认行为,但在iOS 5上,我找不到实现这一点的方法。你有什么建议吗?

我注意到这个问题在iOS 5中存在,并且在iOS 6中也遇到了同样的问题。 - arlomedia
我刚刚将此作为错误报告12540692提交。 - arlomedia
2个回答

2

我认为这是iOS5中的一个bug。我尝试了从其他模态视图控制器中呈现模态视图控制器的实验,发现第二个模态永远不会使屏幕变暗。我甚至设置了一个测试项目,允许您从彼此无限地启动模态,并且似乎每个其他的模态都没有按预期的那样变暗或阻止触摸。

快速在UIWindow的子视图上进行NSLog显示,虽然阴影适当地添加,但遮罩视图却没有被添加。我正在寻找一种方法来展示自己的遮罩视图。找到方法后,我会更新这个答案。

Window Subviews: (
    "<UILayoutContainerView: 0xf63e3c0; frame = (0 0; 768 1024); transform = [0, 1, -1, 0, 0, 0]; autoresize = W+H; layer = <CALayer: 0xf645640>>",
    "<UIDimmingView: 0xf683990; frame = (0 0; 768 1024); opaque = NO; layer = <CALayer: 0xf6836d0>>",
    "<UIDropShadowView: 0xf683130; frame = (64 64; 640 896); transform = [0, 1, -1, 0, 0, 0]; autoresize = W+H; layer = <CALayer: 0xf6831c0>>",
    "<UIDropShadowView: 0x292110; frame = (74 242; 620 540); transform = [0, 1, -1, 0, 0, 0]; autoresize = LM+RM+TM+BM; layer = <CALayer: 0x292150>>"
)

解决方案二: 所以我的最终解决方案是为了获得动画效果。同时,我考虑到了第一个解决方案,但从技术上讲,这可能会激怒苹果,并导致被拒绝,因为 UIDimmingView 是未经记录的,我们"触及"它了。我向我的视图控制器添加了一个带有所需透明度的背景颜色的 UIView。当我呈现模态时,我对其进行了动画处理,当第二个模态的委托被调用时,我反转了动画。我觉得它看起来非常好。也许需要一些时间和透明度调整,以使其完美无缺,但它正在工作且看起来漂亮。

- (void)viewDidLoad 
{
    dim = [[UIView alloc] init];
    [dim setBackgroundColor:[UIColor colorWithWhite:0.0 alpha:0.35]];
    [dim setAlpha:0.0];
    [dim setUserInteractionEnabled:NO];
    [self.view addSubview:dim];
}

- (void)presentModal
{
    [self.view bringSubviewToFront:dim];
    [dim setFrame:self.view.frame];
    [UIView animateWithDuration:0.25 animations:^{
        [dim setAlpha:1.0];
    }];
}

- (void)modalDelegateFinished
{
    [UIView animateWithDuration:0.25 animations:^{
        [dim setAlpha:0.0];
    }];
}

解决方案一:

好的,这个方案可以工作,但是它的动画效果不够理想。然而,它可以重复利用已有的内容,这可能是一个优点。

- (void)viewDidAppear:(BOOL)animated
{
    // Add a gesture to dismiss the view if tapped outside.
    UIGesture *gesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tappedOutsideView:)];
    [gesture setNumberOfTapsRequired:1];
    [gesture setCancelsTouchesInView:NO];
    [self.view.window addGestureRecognizer:gesture];

    // Move the dimmingview to just below the dropshadow.
    UIView *dim = [self.view.window.subviews objectAtIndex:1];
    [self.view.window insertSubview:dim atIndex:2];
}

- (void)tappedOutsideView:(UITapGestureRecognizer *)sender
{
    if (sender.state == UIGestureRecognizerStateEnded) {
        CGPoint location = [sender locationInView:nil];

        if (![self.view pointInside:[self.view convertPoint:location fromView:self.view.window] withEvent:nil]) {
            // remove the gesture on the window
            [self.view.window removeGestureRecognizer:sender];

            // Move the dimmingview back where it belongs
            UIView *dim = [self.view.window.subviews objectAtIndex:2];
            [self.view.window insertSubview:dim atIndex:1];
        }
    }
}

作为备用方案,将同样的内容放在viewDidDisappear中可能是个好主意。我的完成按钮调用tappedOutside视图,因此我知道手势和dimmingView始终是正确的。但如果您没有以这种方式处理,您可以将其放在viewDidDisappear中。

- (void)viewDidDisappear:(BOOL)animated
{
    // remove the gesture on the window
    for (UIGestureRecognizer *gesture in self.view.window.gestureRecognizers) {
        [self.view.window removeGestureRecognizer:gesture];
    }

    // Move the dimmingview back where it belongs
    UIView *dim = [self.view.window.subviews objectAtIndex:2];
    [self.view.window insertSubview:dim atIndex:1];
}

这是我的经验。现在我不会在页面表单上方显示表单,而是在我的应用程序中到处使用页面表单。 - Tom
感谢您对这个问题进行的实验!我认为方案二更好,因为它是动画的,不会干扰iOS的视图。这个解决方案仍然可以改进,使得不是呈现视图控制器变暗,而是模态视图控制器变暗呈现视图控制器。虽然我不知道是否可能实现这一点。 - Tom
除此之外,我仍然相信这是iOS5中的一个漏洞。如果您在其他两个模态视图控制器之上呈现第三个模态视图控制器,则背景会再次变暗。因此,在iOS中确实发生了一些非常奇怪的事情。 - Tom
同意。这绝对是一个bug,因为只有每隔一个视图缺少UIDimmingView。我进行了一些实验,展示了大约40个模态,并且是每隔一个模态会出现这个问题。presented不会向这些堆栈中添加UIDimmingView。另外,如果模态视图可以使其背景变暗,那将是很好的。我通过向窗口添加UIView来尝试过这种效果,但由于只能在显示模态后添加该视图,所以它不能与模态同时进行动画。但这可能还可以改进。 - Ryan Poolos
方案2看起来不错,但为了使其正常工作,您还需要在presentModal方法中添加setUserInteractionEnabled:YES。 - arlomedia
显示剩余2条评论

0

看起来像是iOS的一个bug,在iOS7中仍然存在。

这是我为了解决它所做的(已在iOS7上测试)。有趣的是,呈现模态视图时不会使呈现者变暗,但在其上方呈现第三个模态视图会。

知道了这一点,我们可以在呈现实际表单模态视图之前先呈现一个虚拟的模态视图。这涉及到最少的代码,并且在技术上符合一般的iOS设计模式。因此,未来出现问题的风险应该是最小的。

首先呈现表单模态视图:

- (void)presentFormSheetModalOverThePageSheetModal
{
    //get the controller I'm trying to present and stick it in a nav controller - as usual
    UIViewController *myModalController = [UIViewController new];
    UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:myModalController];
    navController.modalPresentationStyle = UIModalPresentationFormSheet;

    //create an intermediate controller that will be presented first
    UIViewController *intermediateController = [UIViewController new];
    intermediateController.modalPresentationStyle = UIModalPresentationCurrentContext;
    intermediateController.view.backgroundColor = self.view.backgroundColor;

    //create a snapshot of self (presenting controller)
    UIView *navBar = [self.navigationController.navigationBar snapshotViewAfterScreenUpdates:YES];
    UIView *mainView = [self.view snapshotViewAfterScreenUpdates:YES];
    mainView.center = CGPointMake(mainView.center.x, mainView.center.y + navBar.frame.size.height);
    [intermediateController.view addSubview:navBar];
    [intermediateController.view addSubview:mainView];

    //two step presentation
    [self presentViewController:intermediateController animated:NO completion:^{
        [intermediateController presentViewController:navController animated:YES completion:nil];
    }];
}

然后当需要关闭时(从弹出的模态框中):

- (void)dismissMySelf
{
    //again - two step process to dismiss the this controller and the intermediate controller
    UIViewController *originalPresenter = self.presentingViewController.presentingViewController;
    [self.presentingViewController dismissViewControllerAnimated:YES completion:^{
        [originalPresenter dismissViewControllerAnimated:NO completion:nil];
    }];
}

我不得不进行一些调整,因为快照是静态图像,无法处理旋转,但总的来说对我来说效果很好。希望能帮到你。


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