在关闭另一个模式视图控制器后立即呈现一个模式视图控制器

发布于 2024-09-27 08:31:39 字数 401 浏览 1 评论 0原文

我正在解雇一个模态视图控制器,然后立即呈现另一个,但后者永远不会发生。代码如下:

 [self dismissModalViewControllerAnimated:YES];

 UIImagePickerController *picker = [[UIImagePickerController alloc] init];
 picker.delegate = self;
 picker.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;
 [self presentModalViewController:picker animated:YES];

第一个模态 VC 向下滑动,但新的 picker 永远不会出现。知道发生了什么事吗?

I'm dismissing a modal view controller and then immediately presenting another one, but the latter never happens. Here's the code:

 [self dismissModalViewControllerAnimated:YES];

 UIImagePickerController *picker = [[UIImagePickerController alloc] init];
 picker.delegate = self;
 picker.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;
 [self presentModalViewController:picker animated:YES];

The first modal VC slides down, but the new picker never comes up. Any idea as to what's going on?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

颜漓半夏 2024-10-04 08:31:39

2012 年 8 月更新:

iOS 5 及更高版本引入了更安全的 API,用于在模态使用完成块动画进入/离开位置之后执行操作:

[self presentViewController:myModalVC animated:YES completion:^{}];
[self dismissViewControllerAnimated:YES completion:^{}];

2012 年 8 月之前的答案:

当我忽略模态一然后快速连续地呈现模态二时,我遇到了类似的问题。有时情态二会在情态一被消除后显示,有时情态二根本不会出现,这让我非常难过。

对我来说,这看起来像一个竞争条件...

在呈现模态二的方法的调用者上放置 1 秒以上的延迟,showModalTwo,使得模态二每次在模态一被消除后都出现:

- (void)didDismissModalOne {
    [self performSelector:@selector(showModalTwo:) 
               withObject:someNumber 
               afterDelay:1.0f];
}

这证实了怀疑情态一的消除和情态二的呈现之间存在某种竞争条件。然而,对调用者施加延迟是不优雅的,并且不能保证竞争条件在其他情况下不会再次出现。

问题

结果是 UIViewController 有一个公共属性 modalViewController,它在调用 presentModalViewController:animated: 时设置,并在调用时拆除dismissModalViewControllerAnimated: 被调用。问题是它不会同步拆除,因此可能会在删除 modalViewController 的旧值和按以下方式设置新值之间产生竞争。

  1. 现在情态一。 myViewController.modalViewController 现在指向模式一,
  2. 关闭模式一。拆除 myViewController.modalViewController 的后台进程已启动,但 myViewController.modalViewController 仍指向模态一
  3. 当前模态二,myViewController.modalViewController]现在指向模态二
  4. 系统回调触发,将myViewController.modalViewController设置为nil,这会中断模态二动画的过程,结果是用户永远看不到它。

竞赛从第 2 步开始,并在第 4 步显现。

解决方案

我的解决方案是在呈现模态二的方法上设置一个保护条件,以确保 myViewControoler.modalViewControllernil 在尝试呈现情态二之前。

-(void)showModalTwo:(NSNumber *)aParameter {

    if (self.modalViewController) {        
            [self performSelector:@selector(showModalTwo:)
                       withObject:aParameter 
                       afterDelay:0.1f];
            return;
    }
    // You can now present the second modal safely.
}

工作起来就像一个魅力。更优雅的解决方案可能包括超时。

后脚本

我真的不喜欢这个解决方案的轮询方面。 @Nimrod 建议,在该问题的已接受答案中,您可以从模式一的 viewDidDisappear: 方法安全地启动模式二的呈现。我喜欢这种事件驱动方法的声音,但在我的用例中进行完整实现后,我确认在使用 viewDidDisappear: 内的回调呈现模态二时,竞争条件仍然存在。绝对确定模态二将呈现的唯一方法是在父视图控制器内部进行轮询,直到您完全确定 self.modalViewControllernil。只有这样,弹出模态二才是“安全”的。

Aug 2012 Update:

iOS 5 and greater have introduced safer APIs for doing things after modals have animated into / out of place using completion blocks:

[self presentViewController:myModalVC animated:YES completion:^{}];
[self dismissViewControllerAnimated:YES completion:^{}];

Pre-Aug 2012 Answer:

I encountered a similar problem when dismissing modal one and then presenting modal two in rapid succession. Sometimes modal two would show after the modal one was dismissed and sometimes modal two wouldn't appear at all and that made me very sad.

Looks like a race condition to me...

Putting a 1+ second delay on the caller of the method that presented modal two, showModalTwo, made modal two appear every time after modal one was dismissed:

- (void)didDismissModalOne {
    [self performSelector:@selector(showModalTwo:) 
               withObject:someNumber 
               afterDelay:1.0f];
}

This confirmed a suspicion that there was some sort of race condition between the dismissal of modal one and the presentation of modal two. Putting a delay on the caller, however, is inelegant and did not guarantee that the race condition wouldn't re-appear under other circumstances.

The problem

Turns out that UIViewControllers have a public property, modalViewController, that gets set up when presentModalViewController:animated: is called and torn down when dismissModalViewControllerAnimated: is called. The catch is that it doesn't get torn down synchronously, so it's possible to create a race between removing the old value of modalViewController and setting up a new value in the following way.

  1. Present modal one. myViewController.modalViewController now points to modal one
  2. Dismiss modal one. Background process to tear down myViewController.modalViewController has started, but myViewController.modalViewController the still points to modal one
  3. Present modal two, myViewController.modalViewController] now points to modal two
  4. System callback fires, setting myViewController.modalViewController to nil, this interrupts the process of modal two animating in and the result is the user never sees it.

The race starts on step 2 and manifests on step 4.

The solution

My solution was to put a guard condition on the method that presented modal two to ensure that myViewControoler.modalViewController was nil before attempting to present modal two.

-(void)showModalTwo:(NSNumber *)aParameter {

    if (self.modalViewController) {        
            [self performSelector:@selector(showModalTwo:)
                       withObject:aParameter 
                       afterDelay:0.1f];
            return;
    }
    // You can now present the second modal safely.
}

Worked like a charm. A more elegant solution might include a timeout.

Post script

I really didn't like the polling aspect of this solution. @Nimrod suggests, in the accepted answer to this question, that you can safely initiate the presentation of modal two from the viewDidDisappear: method of modal one. I liked the sound of this event driven approach, but after doing a full implementation in my use case I confirmed that the race condition persisted when presenting modal two using a callback inside viewDidDisappear:. The only way to be absolutely sure that modal two will be presented is to poll inside the parent view controller until you're absolutely sure that self.modalViewController is nil. Then and only then is it "safe" to pop modal two.

嘴硬脾气大 2024-10-04 08:31:39

与其他动画事物一样,dismissModalViewControllerAnimated 不会阻塞,直到视图控制器消失。相反,它“开始”视图控制器的解除。您可能需要在模态控制器 1 的 viewDidDisappear 中使用回调,该回调在父视图控制器中调用诸如 modalViewControllerDisappeared 之类的内容。在该方法中,您呈现模态控制器 2。否则机器人 K 所说的。

Like other animated things, dismissModalViewControllerAnimated doesn't block until the view controller disappears. Instead it "kicks off" dismissal of the view controller. You might need to use a callback in viewDidDisappear of modal controller 1 that calls something like modalViewControllerDisappeared in the parent view controller. In that method you present modal controller 2. Otherwise what Robot K said.

终止放荡 2024-10-04 08:31:39
[self dismissViewControllerAnimated:YES completion:^{
    //Present the new MVC 

}];

注意:适用于 iOS 5.0 及以上版本。

[self dismissViewControllerAnimated:YES completion:^{
    //Present the new MVC 

}];

Note: Available iOS 5.0 onwards.

奶茶白久 2024-10-04 08:31:39
[self dismissModalViewControllerAnimated:NO];

 UIImagePickerController *picker = [[UIImagePickerController alloc] init];
 picker.delegate = self;
 picker.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;
 [self presentModalViewController:picker animated:YES];
[self dismissModalViewControllerAnimated:NO];

 UIImagePickerController *picker = [[UIImagePickerController alloc] init];
 picker.delegate = self;
 picker.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;
 [self presentModalViewController:picker animated:YES];
一抹苦笑 2024-10-04 08:31:39

发生的情况是,一旦关闭动画完成,视图控制器就会删除它对模态视图控制器的引用,这是在调用此代码之后发生的,因此它不认为它有一个新的视图控制器可以以模态方式呈现。

我处理这个问题的方法是在调用 dismissModalViewController 后将 didDismissModalVC ivar 设置为 YES。然后在我的 viewDidAppear: 方法中,我检查 ivar 的值并呈现新的模式视图控制器。 (记住还要将该值设置回NO,这样我就不会永远陷入拒绝模态视图控制器的困境。)

What's happening is that the view controller removes it's reference to the modal view controller once the dismiss animation is done, which happens after this code is called, so it doesn't think it has a new view controller to present modally.

How I've dealt with this is to set a didDismissModalVC ivar to YES after I call dismissModalViewController. Then in my viewDidAppear: method, I check the value of the ivar and present then new modal view controller. (Remembering to also set the value back to NO so I don't get stuck eternally dismissing modal view controllers.)

爱要勇敢去追 2024-10-04 08:31:39

在本例中,我创建回调父视图控制器的委托以显示第二个模式视图控制器。

父视图控制器的定义协议:

@protocol ParentViewControllerDelegate
- (void)showModalTwo;
@end

我在父视图控制器中实现此协议以显示第二个模态视图控制器并创建委托属性 @property id; delegate; 在第一个模式视图控制器上。

从父视图控制器显示第一个模态视图控制器:

TheFirstModalViewController *controller = ...
controller.delegate = self;
[self presentViewController:controller animated:YES completion:nil];
...

在第一个模态视图控制器的 viewDidDisappear: 方法中,只需调用 delegate.showModalTwo: 即可从父视图显示第二个模态视图视图控制器。

希望这有帮助。

In this case, I create delegate to callback parent view controller to show the second modal view controller.

Definition protocol of the parent view controller:

@protocol ParentViewControllerDelegate
- (void)showModalTwo;
@end

I implement this protocol in the parent view controller to show the second modal view controller and create the delegate property @property id<ParentViewControllerDelegate> delegate; on the first modal view controller.

Show the first modal view controller from parent view controller:

TheFirstModalViewController *controller = ...
controller.delegate = self;
[self presentViewController:controller animated:YES completion:nil];
...

On viewDidDisappear: method of the first modal view controller, just call delegate.showModalTwo: to show the second modal view from parent view controller.

Hope this help.

月下凄凉 2024-10-04 08:31:39

在 Swift 中:

  1. 使用 DismissViewController 关闭第一个呈现的视图。
  2. 使用missViewController的完成块 调用 mainVC 中的函数。该函数应该调用第二个 VC。

你的missViewController应该看起来像这样:

var presentingVC_Delegate: mainLists_PopoverDelegation!

@IBAction fund button_Pressed (sender: AnyObject) {
    self.dismissViewControllerAnimated(true, completion: { finished in
        self.presentingVC_Delegate.presentOtherVC()
        print("DismissVC completion block says hello")
    })
}

mainVC包含presentOtherVC的地方:

func presentSettingsVC () {
    self.performSegueWithIdentifier("present_OtherVC", sender: nil)
}

In Swift:

  1. Use dismissViewController to dismiss the 1st presented view.
  2. Use dismissViewController's completion block to call a function in the mainVC. That function should call the second VC.

Your dismissViewController should look like this:

var presentingVC_Delegate: mainLists_PopoverDelegation!

@IBAction fund button_Pressed (sender: AnyObject) {
    self.dismissViewControllerAnimated(true, completion: { finished in
        self.presentingVC_Delegate.presentOtherVC()
        print("DismissVC completion block says hello")
    })
}

Where the mainVC houses that presentOtherVC:

func presentSettingsVC () {
    self.performSegueWithIdentifier("present_OtherVC", sender: nil)
}
厌味 2024-10-04 08:31:39

这是我的方法,似乎在 iOS 10 上运行良好。我的情况略有不同,但应该适用于大多数情况。我将初始 viewController 作为弹出窗口呈现,这需要立即呈现模态 viewController。

首先,在初始 viewController 的 viewDidLoad 中简单地隐藏它的视图:

   view.isHidden = true

然后,在它的 viewWillAppear 上,呈现模态视图控制器,未动画并在完成时取消隐藏视图:

   present(yourModalViewController, animated: false) { [unowned self]
       self.view.isHidden = false
    }

您将可能想用 Bool 控制您的状态,以便后续调用 viewWillAppear 不会重新呈现模式,但您明白了。

Here's my approach that seems to work nicely on iOS 10. My circumstance is slightly different, but should work for most situations. I'm presenting the initial viewController as a popover which requires a modal viewController to be presented immediately.

First, in the initial viewController's viewDidLoad simply hide it's view:

   view.isHidden = true

Then, on it's viewWillAppear, present the modal viewController, unanimated and un-hide the view on completion:

   present(yourModalViewController, animated: false) { [unowned self]
       self.view.isHidden = false
    }

You'll probably want to control your state with a Bool so that subsequent calls to viewWillAppear don't re-present the modal, but you get the idea.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文