長いView Controller階層があります。
最初のView Controllerでこのコードを使用します:
SecondViewController *svc = [[SecondViewController alloc] initWithNibName:@"SecondViewController" bundle:nil];
[self presentModalViewController:svc animated:YES];
[svc release];
2番目のView Controllerでは、次のコードを使用します。
ThirdViewController *tvc = [[ThirdViewController alloc] initWithNibName:@"ThirdViewController" bundle:nil];
[self presentModalViewController:tvc animated:YES];
[tvc release];
等々。
そのため、多くのView Controllerがあり、最初のView Controllerに戻る必要がある瞬間があります。一度に1ステップ戻った場合、すべてのView Controllerで次のコードを使用します。
[self dismissModalViewControllerAnimated:YES];
たとえば、6番目のView Controllerから最初のView Controllerに直接戻りたい場合、すべてのControllerを一度に閉じるにはどうすればよいですか?
ありがとう
私は解決策を見つけました。
もちろん、最も明白な場所で解決策を見つけることができるので、dismissModalViewControllerAnimatedメソッドのUIViewControllerリファレンスを読むと...
複数のモーダルビューコントローラーを連続して提示して、モーダルビューコントローラーのスタックを構築する場合、スタックの下位のビューコントローラーでこのメソッドを呼び出すと、その直接の子ビューコントローラーとスタック上のその子より上のすべてのビューコントローラーが破棄されます。これが発生すると、一番上のビューのみがアニメーション形式で閉じられます。中間のView Controllerはスタックから単に削除されます。一番上のビューは、モーダル遷移スタイルを使用して閉じられます。これは、スタック内の下位の他のView Controllerで使用されるスタイルとは異なる場合があります。
そのため、ターゲットビューでdismissModalViewControllerAnimatedを呼び出すだけで十分です。次のコードを使用しました。
[[[[[self parentViewController] parentViewController] parentViewController] parentViewController] dismissModalViewControllerAnimated:YES];
家に帰ります.
- (void)dismissModalStackAnimated:(bool)animated completion:(void (^)(void))completion {
UIView *fullscreenSnapshot = [[UIApplication sharedApplication].delegate.window snapshotViewAfterScreenUpdates:false];
[self.presentedViewController.view insertSubview:fullscreenSnapshot atIndex:NSIntegerMax];
[self dismissViewControllerAnimated:animated completion:completion];
}
func dismissModalStack(animated: Bool, completion: (() -> Void)?) {
if let fullscreenSnapshot = UIApplication.shared.delegate?.window??.snapshotView(afterScreenUpdates: false) {
presentedViewController?.view.addSubview(fullscreenSnapshot)
}
if !isBeingDismissed {
dismiss(animated: animated, completion: completion)
}
}
他のソリューションの何が問題になっていますか?
多くの解決策がありますが、それらのいずれも間違った却下コンテキストとはみなされません:
例えばルートA->プレゼンツB->プレゼンツCで、CからAに却下したい場合は、dismissViewControllerAnimated
でrootViewController
を呼び出して公式にできます。
[[UIApplication sharedApplication].delegate.window.rootViewController dismissModalStackAnimated:true completion:nil];
ただしこのルートでのCからの呼び出し解除は、誤った遷移を伴う正しい動作につながります(CからAの代わりにBからAが表示されます)。
so
普遍的な却下メソッドを作成しました。このメソッド現在のフルスクリーンスナップショットを取得し、受信者の提示されたView Controller上に配置し、それをすべて却下します(例:デフォルトの呼び出しをCから呼び出しますが、Bは本当に却下されているように見える)
最初のView Controllerがルート/初期View Controller(ストーリーボードで初期View Controllerとして指定したもの)でもあるとします。要求をリッスンするように設定して、表示されているすべてのView Controllerを削除できます。
firstViewController:
- (void)viewDidLoad {
[super viewDidLoad];
// listen to any requests to dismiss all stacked view controllers
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(dismissAllViewControllers:) name:@"YourDismissAllViewControllersIdentifier" object:nil];
// the remainder of viewDidLoad ...
}
// this method gets called whenever a notification is posted to dismiss all view controllers
- (void)dismissAllViewControllers:(NSNotification *)notification {
// dismiss all view controllers in the navigation stack
[self dismissViewControllerAnimated:YES completion:^{}];
}
そして、ナビゲーションスタックの一番上に戻るべきであると決定するナビゲーションスタックの下にある他のView Controllerで:
[[NSNotificationCenter defaultCenter] postNotificationName:@"YourDismissAllViewControllersIdentifier" object:self];
これにより、モーダルで表示されたすべてのView Controllerがアニメーションで閉じられ、ルートView Controllerのみが残ります。これは、最初のView ControllerがUINavigationControllerであり、最初のView ControllerがルートView Controllerとして設定されている場合にも機能します。
ボーナスのヒント:通知名が同じであることが重要です。入力エラーによる誤解を避けるために、アプリ内のどこかにこの通知名を変数として定義することをお勧めします。
[[self presentingViewController]presentingViewController]dismissModalViewControllerAnimated:NO];
却下するすべてのコントローラーにデリゲートを実装することもできます
すべてがモデルビューコントローラーを使用している場合は、事前設定されたすべてのビューコントローラーを閉じるための通知を使用できます。
1.このようにRootViewControllerに通知を登録する
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(dismissModelViewController)
name:dismissModelViewController
object:nil];
2. rootviewControllerでdismissModelViewController関数を実装する
- (void)dismissModelViewController
{
While (![self.navigationController.visibleViewController isMemberOfClass:[RootviewController class]])
{
[self.navigationController.visibleViewController dismissViewControllerAnimated:NO completion:nil];
}
}
3.ボタンイベントを閉じるか閉じるたびに通知が投稿されます。
[[NSNotificationCenter defaultCenter] postNotificationName:dismissModelViewController object:self];
Swiftの場合:
self.presentingViewController?.presentingViewController?.dismissViewControllerAnimated(true, completion: nil)
ほとんどのソリューションの問題は、表示されたviewControllerのスタックを閉じると、スタック内の最初に表示されたviewControllerが表示されている間、ユーザーに表示されることです。 Jakubの優れたソリューションはそれを解決します。これが彼の答えに基づいた拡張です。
extension UIViewController {
func dismissAll(animated: Bool, completion: (() -> Void)? = nil) {
if let optionalWindow = UIApplication.shared.delegate?.window, let window = optionalWindow, let rootViewController = window.rootViewController, let presentedViewController = rootViewController.presentedViewController {
if let snapshotView = window.snapshotView(afterScreenUpdates: false) {
presentedViewController.view.addSubview(snapshotView)
presentedViewController.modalTransitionStyle = .coverVertical
}
if !isBeingDismissed {
rootViewController.dismiss(animated: animated, completion: completion)
}
}
}
}
使用法:ルートに戻して表示するviewControllerからこの拡張関数を呼び出します。
@IBAction func close() {
dismissAll(animated: true)
}
これを試して..
ThirdViewController *tvc = [[ThirdViewController alloc] initWithNibName:@"ThirdViewController" bundle:nil];
[self.view addsubview:tvc];
[tvc release];
A Swift this コメントに基づいて追加されたバージョン
func dismissModalStack(viewController: UIViewController, animated: Bool, completionBlock: BasicBlock?) {
if viewController.presentingViewController != nil {
var vc = viewController.presentingViewController!
while (vc.presentingViewController != nil) {
vc = vc.presentingViewController!;
}
vc.dismissViewControllerAnimated(animated, completion: nil)
if let c = completionBlock {
c()
}
}
}
単純な再帰的クローザー:
extension UIViewController {
final public func dismissEntireStackAndSelf(animate: Bool = true) {
// Always false on non-calling controller
presentedViewController?.ip_dismissEntireStackAndSelf(false)
self.dismissViewControllerAnimated(animate, completion: nil)
}
}
これにより、すべての子コントローラーが強制的に閉じられ、自己のみがアニメーション化されます。好きなように切り替えることができますが、各コントローラーをアニメーション化すると、それらは1つずつ移動し、遅くなります。
コール
baseController.dismissEntireStackAndSelf()
ルートView Controllerに戻るために、すべてのView Controllerをポップして閉じるために使用するソリューションを次に示します。 UIViewControllerのカテゴリにこれらの2つのメソッドがあります。
+ (UIViewController*)topmostViewController
{
UIViewController* vc = [[[UIApplication sharedApplication] keyWindow] rootViewController];
while(vc.presentedViewController) {
vc = vc.presentedViewController;
}
return vc;
}
+ (void)returnToRootViewController
{
UIViewController* vc = [UIViewController topmostViewController];
while (vc) {
if([vc isKindOfClass:[UINavigationController class]]) {
[(UINavigationController*)vc popToRootViewControllerAnimated:NO];
}
if(vc.presentingViewController) {
[vc dismissViewControllerAnimated:NO completion:^{}];
}
vc = vc.presentingViewController;
}
}
それから私はただ電話する
[UIViewController returnToRootViewController];
上記の回答に基づいたSwift拡張:
extension UIViewController {
func dismissUntilAnimated<T: UIViewController>(animated: Bool, viewController: T.Type, completion: ((viewController: T) -> Void)?) {
var vc = presentingViewController!
while let new = vc.presentingViewController where !(new is T) {
vc = new
}
vc.dismissViewControllerAnimated(animated, completion: {
completion?(viewController: vc as! T)
})
}
}
Swift 3.0バージョン:
extension UIViewController {
/// Dismiss all modally presented view controllers until a specified view controller is reached. If no view controller is found, this function will do nothing.
/// - Parameter reached: The type of the view controller to dismiss until.
/// - Parameter flag: Pass `true` to animate the transition.
/// - Parameter completion: The block to execute after the view controller is dismissed. This block contains the instance of the `presentingViewController`. You may specify `nil` for this parameter.
func dismiss<T: UIViewController>(until reached: T.Type, animated flag: Bool, completion: ((T) -> Void)? = nil) {
guard let presenting = presentingViewController as? T else {
return presentingViewController?.dismiss(until: reached, animated: flag, completion: completion) ?? ()
}
presenting.dismiss(animated: flag) {
completion?(presenting)
}
}
}
ほとんどの場合、モーダルビューコントローラーの表示ビューコントローラーがUITabBarController
これをまったく役に立たないことを考えると、これが信じられないほど愚かなロジックであるため、これを作成した理由を完全に忘れてしまいました。実際にベースビューコントローラーインスタンスを取得し、その上でdismiss
を呼び出す方がはるかに理にかなっています。
id vc = [self presentingViewController];
id lastVC = self;
while (vc != nil) {
id tmp = vc;
vc = [vc presentingViewController];
lastVC = tmp;
}
[lastVC dismissViewControllerAnimated:YES completion:^{
}];
Swift上記の回答に基づく拡張。
そのようなスタックの原理:A-> B-> C-> D
完了したら、Aをアニメーションで閉じます
extension UIViewController {
func dismissModalStack(animated: Bool, completion: (() -> Void)?) {
let fullscreenSnapshot = UIApplication.shared.delegate?.window??.snapshotView(afterScreenUpdates: false)
if !isBeingDismissed {
var rootVc = presentingViewController
while rootVc?.presentingViewController != nil {
rootVc = rootVc?.presentingViewController
}
let secondToLastVc = rootVc?.presentedViewController
if fullscreenSnapshot != nil {
secondToLastVc?.view.addSubview(fullscreenSnapshot!)
}
secondToLastVc?.dismiss(animated: false, completion: {
rootVc?.dismiss(animated: true, completion: completion)
})
}
}
}
シミュレータでは少しちらつきますが、デバイスではちらつきません。
まず、オスカーペリはコードをありがとう。
最初にnavigationControllerを起動するには、この方法でもう少し動的にすることができます。 (スタック内のViewControllerの数がわからない場合)
NSArray *viewControllers = self.navigationController.viewControllers;
[self.navigationController popToViewController: [viewControllers objectAtIndex:0] animated: YES];
Swift 3.0 +の場合
self.view.window!.rootViewController?.dismissViewControllerAnimated(false, completion: nil)
これにより、rootviewcontrollerで表示されているすべてのView Controllerが閉じられます。
この一般的なソリューションを使用して、この問題を解決します。
- (UIViewController*)topViewController
{
UIViewController *topController = [UIApplication sharedApplication].keyWindow.rootViewController;
while (topController.presentedViewController) {
topController = topController.presentedViewController;
}
return topController;
}
- (void)dismissAllModalController{
__block UIViewController *topController = [self topViewController];
while (topController.presentingViewController) {
[topController dismissViewControllerAnimated:NO completion:^{
}];
topController = [self topViewController];
}
}
上部のVCアニメーション化されたものとそうでないものを削除します。
[self dismissModalViewControllerAnimated:NO]; // First
[self dismissModalViewControllerAnimated:NO]; // Second
[self dismissModalViewControllerAnimated:YES]; // Third
編集:1つの方法だけでこれを行いたい場合は、階層をVCの配列に保存し、最後にアニメーション化されたオブジェクトとそれ以外のオブジェクトは破棄します。
dismiss(animated:completion:) メソッドに関するAppleドキュメント。
セクションDiscussion
では、次のように述べました。
any intermediate view controllers are simply removed from the stack.
複数のView Controllerを連続して提示し、提示されたView Controllerのスタックを構築する場合、スタックの下位のView Controllerでこのメソッドを呼び出すと、その直接の子View Controllerとスタック上のその子より上のすべてのView Controllerが破棄されます。これが発生すると、一番上のビューのみがアニメーション形式で閉じられます。 中間のView Controllerはすべてスタックから削除されます。最上位のビューは、モーダル遷移スタイルを使用して閉じられます。スタックの下位にある他のView Controller。
つまり、View Controllerが次のようにスタックしている場合
Root -> A -> B -> C -> D ... -> Z
D
はdismiss
メソッドを呼び出し、すべてのView ControllerはD
を非表示にします。例:(E ... Z)
、スタックから削除されます。