web-dev-qa-db-ja.com

別を閉じた直後にモーダルビューコントローラーを表示する

モーダルビューコントローラーを閉じてすぐに別のビューコントローラーを表示していますが、後者は発生しません。これがコードです:

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

最初のモーダルVCは下にスライドしますが、新しいpickerは表示されません。何が起こっているのかについてのアイデアはありますか?

38
James Skidmore

他のアニメーションのものと同様に、dismissModalViewControllerAnimatedは、ビューコントローラが消えるまでブロックしません。代わりに、それはビューコントローラの却下を「キックオフ」します。親ビューコントローラーでviewDidDisappearのようなものを呼び出すモーダルコントローラー1のmodalViewControllerDisappearedでコールバックを使用する必要がある場合があります。その方法では、モーダルコントローラー2を提示します。それ以外の場合は、ロボットKが言ったことです。

16
Nimrod

2012年8月更新:

iOS 5以降では、完了ブロックを使用してモーダルが /の場所でアニメーション化された後で、より安全なAPIが導入されています。

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

2012年8月以前の回答:

モーダル1を閉じてからモーダル2を続けて表示すると、同様の問題が発生しました。モーダル1が却下された後にモーダル2が表示されることもあれば、モーダル2がまったく表示されないこともあり、とても悲しかったです。

私には競合状態のように見えます...

モーダル2を提示したメソッドの呼び出し元にshowModalTwoを1秒以上遅延させると、モーダル1が閉じられた後、毎回モーダル2が表示されます。

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

これは、モーダル1の却下とモーダル2の提示の間に何らかの競合状態があったという疑いを確認しました。ただし、呼び出し側に遅延を設けることは洗練されておらず、他の状況下で競合状態が再発しないことを保証するものではありませんでした。

問題

UIViewControllersには、presentModalViewController:animated:が呼び出されたときに設定され、dismissModalViewControllerAnimated:が呼び出されたときに破棄されるパブリックプロパティmodalViewControllerがあることがわかります。問題は、同期的に破棄されないことです。そのため、modalViewControllerの古い値を削除してから、次の方法で新しい値を設定するまでの間に競合が発生する可能性があります。

  1. モーダルワンを提示します。 myViewController.modalViewControllerがモーダル1を指すようになりました
  2. モーダルを閉じます。 myViewController.modalViewControllerを破棄するバックグラウンドプロセスが開始されましたが、myViewController.modalViewControllerはまだモーダルをポイントしています
  3. 現在のモーダル2、myViewController.modalViewController]がモーダル2を指すようになりました
  4. システムコールバックが発生し、myViewController.modalViewControllernilに設定します。これにより、モーダル2がアニメートするプロセスが中断され、ユーザーが目にすることはありません。

レースはステップ2から始まり、ステップ4に現れます。

ソリューション

私の解決策は、モーダル2を表すメソッドにガード条件を設定して、モーダル2を表す前に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は、この質問に対する受け入れられた回答で、モーダル1のviewDidDisappear:メソッドからモーダル2のプレゼンテーションを安全に開始できることを示唆しています。このイベントドリブンアプローチのサウンドは気に入りましたが、私のユースケースで完全な実装を行った後、viewDidDisappear:内のコールバックを使用してモーダル2を提示すると、競合状態が持続することを確認しました。モーダル2が表示されることを確実に確認する唯一の方法は、self.modalViewControllernilであることが確実にわかるまで、親View Controller内でポーリングすることです。そのときだけ、モーダル2をポップしても「安全」です。

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

}];

注:iOS 5.0以降で利用できます。

15
Ilker Baltaci
[self dismissModalViewControllerAnimated:NO];

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

何が起こっているのかというと、アニメーションを閉じると、ビューコントローラーがモーダルビューコントローラーへの参照を削除するためです。これは、このコードが呼び出された後に行われるため、モーダルで表示する新しいビューコントローラーがあるとは考えられません。

これに対処するには、didDismissModalVCを呼び出した後、YES ivarをdismissModalViewControllerに設定します。それから私のviewDidAppear:メソッドでは、ivarの値を確認し、新しいモーダルビューコントローラーを表示しています。 (値をNOに戻すことも忘れないでください。そうすれば、永久にモーダルビューコントローラーを閉じてしまうのを防ぐことができます。)

2
Kris Markel

この場合、2番目のモーダルビューコントローラーを表示するために、デリゲートからコールバックの親ビューコントローラーを作成します。

親View Controllerの定義プロトコル:

@protocol ParentViewControllerDelegate
- (void)showModalTwo;
@end

このプロトコルを親ビューコントローラーに実装して2番目のモーダルビューコントローラーを表示し、最初のモーダルビューコントローラーにデリゲートプロパティ@property id<ParentViewControllerDelegate> delegate;を作成します。

親ビューコントローラーから最初のモーダルビューコントローラーを表示します。

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

最初のモーダルビューコントローラーのviewDidDisappear:メソッドでdelegate.showModalTwo:を呼び出すだけで、親ビューコントローラーの2番目のモーダルビューが表示されます。

この助けを願っています。

0
ThanhHH

IOS 10でうまく機能するように見える私のアプローチを以下に示します。私の状況は少し異なりますが、ほとんどの状況で機能するはずです。最初のviewControllerを、モーダルなviewControllerをすぐに表示する必要があるポップオーバーとして表示しています。

まず、最初のviewControllerのviewDidLoadでビューを非表示にします。

   view.isHidden = true

次に、viewWillAppearで、モーダルのビューコントローラーをアニメーション化せずに表示し、完了時にビューを再表示します。

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

Boolへの後続の呼び出しでモーダルが再表示されないようにviewWillAppearを使用して状態を制御することをお勧めしますが、アイデアは得られます。

0
capikaw

Swiftの場合:

  1. 最初に表示されたビューを閉じるには、dismissViewControllerを使用します。
  2. DismissViewControllerの 完了ブロック を使用して、mainVCの関数を呼び出します。その関数は2番目のVCを呼び出す必要があります。

DismissViewControllerは次のようになります。

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)
}
0
Dave G