iPhone - 複数の ViewController を閉じる
-
05-10-2019 - |
質問
長いビュー コントローラー階層があります。
最初の 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 番目のビュー コントローラーから最初のビュー コントローラーに直接戻りたい場合、すべてのコントローラーを一度に閉じるにはどうすればよいでしょうか?
ありがとう
解決 2
解決策を見つけました。
もちろん、あなたは最も明白な場所で解決策を見つけることができるので、DismissModalviewControllerAnimated MethodのUiviewControllerリファレンスから読むことができます...
いくつかのモーダルビューコントローラーを連続して提示し、モーダルビューコントローラーのスタックを構築する場合、スタックの低いビューコントローラーでこのメソッドを呼び出すと、その即時の子ビューコントローラーとスタック上の子供の上のすべてのビューコントローラーが却下されます。これが起こると、最上位のビューのみがアニメーション化された方法で却下されます。中間ビューコントローラーは、スタックから単純に削除されます。最上位ビューは、モーダルトランジションスタイルを使用して却下されます。これは、スタックの低い他のビューコントローラーが使用するスタイルとは異なる場合があります。
したがって、ターゲットビューでdismissmodalviewcontrollerAnimatedを呼び出すだけで十分です。次のコードを使用しました。
[[[[[self parentViewController] parentViewController] parentViewController] parentViewController] dismissModalViewControllerAnimated:YES];
私の家に戻る。
他のヒント
はい。すでにたくさんの回答がありますが、とにかくリストの最後に 1 つだけ追加します。問題は、階層の基部にあるビュー コントローラーへの参照を取得する必要があることです。@Juan Munhoes Juniorの回答と同様に、階層をたどることはできますが、ユーザーが選択できるルートは異なる可能性があるため、これはかなり脆弱な回答です。この単純なソリューションを拡張するのは難しくありませんが、階層をたどってスタックの最下位を探すだけです。一番下のもので dismiss を呼び出すと、他のすべても取得されます。
-(void)dismissModalStack {
UIViewController *vc = self.presentingViewController;
while (vc.presentingViewController) {
vc = vc.presentingViewController;
}
[vc dismissViewControllerAnimated:YES completion:NULL];
}
これはシンプルで柔軟です。スタック内で特定の種類のビュー コントローラーを探したい場合は、以下に基づいてロジックを追加できます。 [vc isKindOfClass:[DesiredViewControllerClass class]]
.
間違ったアニメーションコンテキストなしでフルスクリーン解雇のためのiOS 8+ユニバーサル方法。 Objective-CおよびSwiftで
Objective-C
- (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)
}
}
tl; dr
他のソリューションの何が問題になっていますか?
多くの解決策がありますが、それらのどれも間違った却下のコンテキストでカウントされないので:
例えば root a-> sentents b-> contents c そして、あなたはcからAに却下したい、あなたは電話することで正式にすることができます dismissViewControllerAnimated
の上 rootViewController
.
[[UIApplication sharedApplication].delegate.window.rootViewController dismissModalStackAnimated:true completion:nil];
でも Cからのこのルートでのコール却下は、誤った遷移で正しい動作につながります(bからAの代わりにAからAが見られたはずです)。
それで
Universal Dismiss Methodを作成しました。この方法 現在のフルスクリーンのスナップショットを撮影し、レシーバーの提示されたビューコントローラーの上に置き、すべてを却下します。 (例:cからのデフォルトの解雇と呼ばれるが、Bは本当に却下されていると見なされている)
最初のビューコントローラーは、ルート /初期ビューコントローラー(ストーリーボードで最初のビューコントローラーとしてノミネートしたコントローラー)でもあるとします。提示されたすべてのビューコントローラーを却下するリクエストを聞くように設定できます。
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:^{}];
}
ナビゲーションスタックの下にある他のビューコントローラーでは、ナビゲーションスタックの上部に戻る必要があります。
[[NSNotificationCenter defaultCenter] postNotificationName:@"YourDismissAllViewControllersIdentifier" object:self];
これにより、モダンで提示されたすべてのビューコントローラーをアニメーションで却下し、ルートビューコントローラーのみを残します。これは、初期ビューコントローラーがUinavigationControllerであり、最初のビューコントローラーがルートビューコントローラーとして設定されている場合にも機能します。
ボーナスヒント:通知名が同一であることが重要です。おそらく、この通知名をアプリのどこかに変数として定義することをお勧めします。
[[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)
ほとんどのソリューションの問題は、提示されたViewControllersのスタックを却下すると、ユーザーが却下されているときに最初に提示された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)
}
}
}
}
使用法:rootに却下したい提示されたViewControllerからこの拡張機能を呼び出します。
@IBAction func close() {
dismissAll(animated: true)
}
これを試して..
ThirdViewController *tvc = [[ThirdViewController alloc] initWithNibName:@"ThirdViewController" bundle:nil];
[self.view addsubview:tvc];
[tvc release];
まず第一に、オスカーペリコードをありがとう。
最初にNavigationControllerを起動するには、このようにもう少し動的にすることができます。 (スタック内のviewcontrollersの数がわからない場合)
NSArray *viewControllers = self.navigationController.viewControllers;
[self.navigationController popToViewController: [viewControllers objectAtIndex:0] animated: YES];
id vc = [self presentingViewController];
id lastVC = self;
while (vc != nil) {
id tmp = vc;
vc = [vc presentingViewController];
lastVC = tmp;
}
[lastVC dismissViewControllerAnimated:YES completion:^{
}];
ルートビューコントローラーに戻るために、すべてのビューコントローラーをポップして却下するために使用するソリューションを次に示します。これらの2つの方法は、uiviewcontrollerのカテゴリにあります。
+ (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];
に基づいていくつかの追加を備えた迅速なバージョン これ コメント
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()
スウィフト3 上記の回答に基づく拡張機能。
そのようなスタックの原則:a-> b-> c-> d
- dのスナップショットを取ります
- bにこのスナップショットを追加します
- アニメーションなしでBから却下します
完了時に、アニメーションで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) }) } } }
シミュレーターでは少しちらつきますが、デバイスではちらつきません。
上記の回答に基づく迅速な拡張機能:
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)
}
}
}
ほとんどの場合、Modal View Controllerの提示ビューコントローラーは非常に愚かなロジックであるため、なぜ私がこれを作ったのかを完全に忘れました。 UITabBarController
これを完全に役に立たないようにします。ベースビューコントローラーインスタンスを実際に取得して呼び出す方がはるかに理にかなっています dismiss
その上。
Swift 3.0+の場合
self.view.window!.rootViewController?.dismissViewControllerAnimated(false, completion: nil)
これにより、RootViewControllerで提示されたすべてのビューコントローラーが却下されます。
この一般的なソリューションを使用して、この問題を解決します。
- (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を却下しますが、他のVCを却下しません。 3つのモーダルVCをヘイスする場合
[self dismissModalViewControllerAnimated:NO]; // First
[self dismissModalViewControllerAnimated:NO]; // Second
[self dismissModalViewControllerAnimated:YES]; // Third
編集:1つのメソッドでのみこれを実行する場合は、階層をVCの配列に保存し、最後のオブジェクトをアニメーション化し、他のオブジェクトを却下します。
Appleドキュメントについて 却下(アニメーション:完了:) 方法。
セクションで Discussion
, 、 と言いました:
any intermediate view controllers are simply removed from the stack.
いくつかのビューコントローラーを連続して提示して、提示されたビューコントローラーのスタックを構築する場合、スタックの低いビューコントローラーでこのメソッドを呼び出すと、その即時の子ビューコントローラーとスタック上の子供の上のすべてのビューコントローラーが却下されます。これが起こると、最上位のビューのみがアニメーション化された方法で却下されます。 中間ビューコントローラーは、スタックから単純に削除されます。 最上位ビューは、モーダルトランジションスタイルを使用して却下されます。これは、スタックの低い他のビューコントローラーが使用するスタイルとは異なる場合があります。
言い換えれば、ビューコントローラーが次のようにスタックする場合
Root -> A -> B -> C -> D ... -> Z
D
電話 dismiss
方法、すべてのビューコントローラーに代わって D
, 、 元: (E ... Z)
, 、スタックから削除されます。
の Swift 4 と Xcode 9 これはあなたに役立ちます。
var vc : UIViewController = self.presentingViewController!
while ((vc.presentingViewController) != nil) {
vc = vc.presentingViewController!
}
vc.dismiss(animated: true, completion: nil)
楽しみ !!! :)
スタートに戻る場合は、コード[self.navigationcontroller poptorootviewcontrollerAnimated:yes]を使用できます。