この行:
[self dismissViewControllerAnimated:YES completion:nil];
はそれ自体にメッセージを送信しているのではなく、実際には提示元の VC にメッセージを送信して、却下を行うように求めています。VC を提示すると、提示する VC と提示される VC の間に関係が作成されます。したがって、提示中の VC を破棄しないでください (提示された VC は、その却下メッセージを送り返すことはできません…)。あなたは本当にそれを考慮していないので、アプリを混乱した状態のままにしています. 私の回答Dismissing a Presented View Controller を参照してください。
このメソッドはより明確に書かれていることをお勧めします。
[self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
あなたの場合、すべての制御が で行われていることを確認する必要がありますmainVC
。デリゲートを使用して ViewController1 から MainViewController に正しいメッセージを送り返す必要があります。これにより、mainVC は VC1 を閉じてから VC2 を提示できます。
VC2 VC1では、.h ファイルの @interface の上にプロトコルを追加します。
@protocol ViewController1Protocol <NSObject>
- (void)dismissAndPresentVC2;
@end
@interface セクションの同じファイルの下部で、デリゲート ポインターを保持するプロパティを宣言します。
@property (nonatomic,weak) id <ViewController1Protocol> delegate;
VC1 .m ファイルでは、却下ボタン メソッドがデリゲート メソッドを呼び出す必要があります。
- (IBAction)buttonPressedFromVC1:(UIButton *)sender {
[self.delegate dissmissAndPresentVC2]
}
mainVC で、VC1 の作成時に VC1 のデリゲートとして設定します。
- (IBAction)present1:(id)sender {
ViewController1* vc = [[ViewController1 alloc] initWithNibName:@"ViewController1" bundle:nil];
vc.delegate = self;
[self present:vc];
}
デリゲート メソッドを実装します。
- (void)dismissAndPresent2 {
[self dismissViewControllerAnimated:NO completion:^{
[self present2:nil];
}];
}
present2:
VC2Pressed:
ボタンの IBAction メソッドと同じメソッドにすることができます。これは、VC1 が完全に破棄されるまで VC2 が表示されないようにするために、完了ブロックから呼び出されることに注意してください。
VC1->VCMain->VC2 から移動しているため、トランジションの 1 つだけをアニメーション化する必要があります。
アップデート
あなたのコメントでは、一見単純なことを達成するために必要な複雑さに驚きを表明しています。この委譲パターンは、Objective-C と Cocoa の多くにとって非常に中心的なものであり、この例は得られる最も単純なものであり、慣れるために本当に努力する必要があります。
Apple のView Controller Programming Guideには、次のように書かれています。
提示されたView Controllerを閉じる
提示されたビュー コントローラーを閉じるときが来たら、推奨される方法は、提示しているビュー コントローラーにそれを閉じさせることです。つまり、可能な限り、View Controller を提示したのと同じ View Controller が、それを破棄する責任も負う必要があります。提示されたView Controllerを破棄する必要があることを提示側のView Controllerに通知する方法はいくつかありますが、推奨される方法は委譲です。詳細については、「委任を使用して他のコントローラーと通信する」を参照してください。</p>
何を達成したいのか、どのようにそれを実現しようとしているのかをよく考えてみると、NavigationController を使用したくない場合、MainViewController にメッセージを送信してすべての作業を行うことが唯一の論理的な方法であることがわかります。NavControllerを使用する場合、実際には、明示的でなくても、すべての作業を navController に「委譲」していることになります。VC ナビゲーションで何が起こっているかを中心に追跡するオブジェクトが必要であり、何をするにしても、それと通信する何らかの方法が必要です。
実際には、Appleのアドバイスは少し極端です...通常の場合、専用のデリゲートとメソッドを作成する必要はありません。信頼できます[self presentingViewController] dismissViewControllerAnimated:
-あなたのようなケースでは、あなたの解任がリモートに他の影響を与えたいときですあなたが世話をする必要があるオブジェクト。
これは、デリゲートの手間をかけずに機能すると想像できるものです...
- (IBAction)dismiss:(id)sender {
[[self presentingViewController] dismissViewControllerAnimated:YES
completion:^{
[self.presentingViewController performSelector:@selector(presentVC2:)
withObject:nil];
}];
}
プレゼンティング コントローラーに私たちを却下するように要求した後、presentingViewController 内のメソッドを呼び出して VC2 を呼び出す完了ブロックがあります。代理人は必要ありません。(ブロックの大きなセールス ポイントは、これらの状況でデリゲートの必要性が減ることです)。ただし、この場合、邪魔になることがいくつかあります...
- VC1では、mainVC がメソッドを実装していることを知りません
present2
。デバッグが困難なエラーやクラッシュが発生する可能性があります。デリゲートは、これを回避するのに役立ちます。
- VC1 が閉じられると、完了ブロックを実行するのは実際にはありません... それともそうですか? self.presentingViewController はもう意味がありますか? あなたは知りません (私も知りません)...デリゲートがあれば、この不確実性はありません。
- このメソッドを実行しようとすると、警告やエラーなしでハングします。
ですから、時間をかけて委任について学んでください。
update2
あなたのコメントでは、VC2 の却下ボタン ハンドラーでこれを使用して、それを機能させることができました。
[self.view.window.rootViewController dismissViewControllerAnimated:YES completion:nil];
これは確かにはるかに簡単ですが、多くの問題が残ります。
密結合
viewController 構造を一緒に配線しています。たとえば、mainVC の前に新しい viewController を挿入すると、必要な動作が壊れます (前の動作に移動します)。VC1 では、VC2 も #import する必要がありました。したがって、非常に多くの相互依存関係があり、OOP/MVC の目的が損なわれます。
デリゲートを使用すると、VC1 も VC2 も mainVC やその前提条件について何も知る必要がないため、すべてを疎結合でモジュール化します。
メモリ
VC1 は消えていません。まだ 2 つのポインターを保持しています。
- mainVCの
presentedViewController
プロパティ
- VC2の
presentingViewController
プロパティ
これは、ロギングによってテストできます。また、VC2 からこれを実行するだけでもテストできます。
[self dismissViewControllerAnimated:YES completion:nil];
それでも機能し、VC1 に戻ります。
それはメモリリークのように思えます。
これの手がかりは、ここで得られる警告にあります。
[self presentViewController:vc2 animated:YES completion:nil];
[self dismissViewControllerAnimated:YES completion:nil];
// Attempt to dismiss from view controller <VC1: 0x715e460>
// while a presentation or dismiss is in progress!
VC2 が提示されたVCである提示 VC を却下しようとしているため、ロジックは崩壊します。2 番目のメッセージは実際には実行されません。おそらく何かが起こるのでしょうが、削除したと思っていたオブジェクトへの 2 つのポインタが残っています。(編集 - これを確認しましたが、それほど悪くはありません。mainVC に戻ると、両方のオブジェクトが消えます)
これはかなり長ったらしい言い方です。デリゲートを使用してください。それが役立つ場合は、ここでパターンの簡単な説明をもう 1 つ作成
しました。コンストラクターでコントローラーを渡すことは常に悪い習慣ですか?
update 3
本当にデリゲートを避けたいのなら、これが最善の方法かもしれません:
VC1:
[self presentViewController:VC2
animated:YES
completion:nil];
しかし、何も無視しないでください...私たちが確認したように、とにかく実際には起こりません.
VC2:
[self.presentingViewController.presentingViewController
dismissViewControllerAnimated:YES
completion:nil];
VC1 を閉じていないことがわかっているので、VC1を介してMainVC に戻ることができます。MainVC は VC1 を却下します。VC1 がなくなったので、VC2 がそれに伴って表示されるので、クリーンな状態で MainVC に戻ります。
VC1 は VC2 について知る必要があり、VC2 は MainVC->VC1 経由で到着したことを知る必要があるため、依然として高度に結合されていますが、明示的な委譲を少し行わなくても得られる最高のものです。