web-dev-qa-db-ja.com

UINavigationControllerからビューをポップし、1つの操作で別のビューに置き換えるにはどうすればよいですか?

UINavigationControllerのスタックから1つのビューを削除し、別のビューに置き換える必要があるアプリケーションがあります。状況は、最初のビューが編集可能なアイテムを作成してから、それ自体をアイテムのエディターに置き換えることです。最初のビュー内で明白な解決策を実行すると:

MyEditViewController *mevc = [[MYEditViewController alloc] initWithGizmo: gizmo];

[self retain];
[self.navigationController popViewControllerAnimated: NO];
[self.navigationController pushViewController: mevc animated: YES];
[self release];

非常に奇妙な動作をします。通常、エディタービューが表示されますが、ナビゲーションバーの戻るボタンを使用しようとすると、余分な画面が表示されます。タイトルもランダムになります。ナビゲーションスタックが完全にホース接続されているようです。

この問題に対するより良いアプローチは何でしょうか?

ありがとう、マット

83
Matt Brandt

viewControllersプロパティを手動で混乱させる必要はまったくないことがわかりました。基本的に、これには2つの注意が必要です。

  1. self.navigationControllerは、nilが現在Navigation Controllerのスタック上にない場合、selfを返します。そのため、アクセスできなくなる前にローカル変数に保存してください。
  2. retain(および適切にreleaseselfする必要があります。そうしないと、現在のメソッドを所有しているオブジェクトの割り当てが解除され、奇妙になります。

準備ができたら、通常どおりポップしてプッシュします。このコードは、すぐにトップコントローラーを別のコントローラーに置き換えます。

// locally store the navigation controller since
// self.navigationController will be nil once we are popped
UINavigationController *navController = self.navigationController;

// retain ourselves so that the controller will still exist once it's popped off
[[self retain] autorelease];

// Pop this controller and replace with another
[navController popViewControllerAnimated:NO];
[navController pushViewController:someViewController animated:NO];

最後の行でanimatedYESに変更すると、新しい画面が実際にアニメーション表示され、ポップしたコントローラーがアニメーション表示されます。いいね!

137
Alex Wayne

次のアプローチは私にとってより良いようであり、ARCでもうまく機能します:

UIViewController *newVC = [[UIViewController alloc] init];
// Replace the current view controller
NSMutableArray *viewControllers = [NSMutableArray arrayWithArray:[[self navigationController] viewControllers]];
[viewControllers removeLastObject];
[viewControllers addObject:newVC];
[[self navigationController] setViewControllers:viewControllers animated:YES];
56
Luke Rogers

経験から、UINavigationControllerのviewControllersプロパティを直接いじる必要があります。このような何かが動作するはずです:

MyEditViewController *mevc = [[MYEditViewController alloc] initWithGizmo: gizmo];

[[self retain] autorelease];
NSMutableArray *controllers = [[self.navigationController.viewControllers mutableCopy] autorelease];
[controllers removeLastObject];
self.navigationController.viewControllers = controllers;
[self.navigationController pushViewController:mevc animated: YES];

注:保持/リリースを保持/自動リリースに変更したのは、一般的に堅牢性が高いためです-保持/リリースの間に例外が発生すると、自己リークが発生しますが、自動リリースがそれを処理します。

9
Lily Ballard

多大な労力をかけて(そしてKevinからコードを調整して)、スタックからポップされているView Controllerでこれを行う方法をようやく見つけました。私が抱えていた問題は、コントローラ配列から最後のオブジェクトを削除した後にself.navigationControllerがnilを返していたことです。これは、インスタンスメソッドnavigationControllerのUIViewControllerのドキュメントにある次の行によるものだと思います。

現在のView Controllerがスタックから削除されると、navigationControllerメソッドはnilを返すと思います。

調整済みのコードは次のとおりです。

UINavigationController *navController = self.navigationController;
MyEditViewController *mevc = [[MYEditViewController alloc] initWithGizmo: gizmo];

NSMutableArray *controllers = [[self.navigationController.viewControllers mutableCopy] autorelease];
[controllers removeLastObject];
navController.viewControllers = controllers;
[navController pushViewController:mevc animated: YES];
7
Michael

おかげで、これはまさに私が必要としていたものでした。また、これをアニメーションに入れてページカールを取得します。

        MyEditViewController *mevc = [[MYEditViewController alloc] initWithGizmo: gizmo];

    UINavigationController *navController = self.navigationController;      
    [[self retain] autorelease];

    [UIView beginAnimations:nil context:NULL]; [UIView setAnimationDuration: 0.7];
    [UIView setAnimationTransition:<#UIViewAnimationTransitionCurlDown#> forView:navController.view cache:NO];

    [navController popViewControllerAnimated:NO];
    [navController pushViewController:mevc animated:NO];

    [UIView commitAnimations];

0.6の持続時間は高速で、3GS以降に適していますが、0.8は3Gにはまだ速すぎます。

ヨハン

4
Johan

PopToRootViewControllerによって他のView Controllerを表示する場合は、次の手順を実行する必要があります。

         UIViewController *newVC = [[WelcomeScreenVC alloc] initWithNibName:@"WelcomeScreenVC" bundle:[NSBundle mainBundle]];
            NSMutableArray *viewControllers = [NSMutableArray arrayWithArray:[[self navigationController] viewControllers]];
            [viewControllers removeAllObjects];
            [viewControllers addObject:newVC];
            [[self navigationController] setViewControllers:viewControllers animated:NO];

これで、以前のすべてのスタックが削除され、必要なrootViewControllerで新しいスタックが作成されます。

3
msmq

私のお気に入りの方法は、UINavigationControllerのカテゴリを使用することです。以下が動作するはずです:

INavigationController + Helpers.h #import

@interface UINavigationController (Helpers)

- (UIViewController*) replaceTopViewControllerWithViewController: (UIViewController*) controller;

@end

INavigationController + Helpers.m
#import "UINavigationController + Helpers.h"

@implementation UINavigationController (Helpers)

- (UIViewController*) replaceTopViewControllerWithViewController: (UIViewController*) controller {
    UIViewController* topController = self.viewControllers.lastObject;
    [[topController retain] autorelease];
    UIViewController* poppedViewController = [self popViewControllerAnimated:NO];
    [self pushViewController:controller animated:NO];
    return poppedViewController;
}

@end

次に、View Controllerから、次のようにしてトップビューを新しいものに置き換えることができます。

[self.navigationController replaceTopViewControllerWithViewController: newController];
1
bbrame

このUINavigationControllerインスタンスメソッドは動作する可能性があります...

指定されたView ControllerがトップView ControllerになるまでView Controllerをポップし、表示を更新します。

- (NSArray *)popToViewController:(UIViewController *)viewController animated:(BOOL)animated
1
diclophis
NSMutableArray *controllers = [self.navigationController.viewControllers mutableCopy];
    for(int i=0;i<controllers.count;i++){
       [controllers removeLastObject];
    }
 self.navigationController.viewControllers = controllers;
1
Ravi

以下は、viewControllers配列を直接操作する必要のない別のアプローチです。コントローラがまだポップされているかどうかを確認し、もしそうなら、それをプッシュします。

TasksViewController *taskViewController = [[TasksViewController alloc] initWithNibName:nil bundle:nil];

if ([navigationController.viewControllers indexOfObject:taskViewController] == NSNotFound)
{
    [navigationController pushViewController:taskViewController animated:animated];
}
else
{
    [navigationController popToViewController:taskViewController animated:animated];
}
1
ezekielDFM

私は最近、同様のことをしなければならなかったし、マイケルズの答えに基づいて私の解決策に基づいていました。私の場合、Navigation Stackから2つのView Controllerを削除してから、新しいView Controllerを追加する必要がありました。呼び出し中

[コントローラremoveLastObject];
UINavigationController *navController = self.navigationController;

// retain ourselves so that the controller will still exist once it's popped off
[[self retain] autorelease];

searchViewController = [[SearchViewController alloc] init];    
NSMutableArray *controllers = [[self.navigationController.viewControllers mutableCopy] autorelease];

[controllers removeLastObject];
// In my case I want to go up two, then Push one..
[controllers removeLastObject];
navController.viewControllers = controllers;

NSLog(@"controllers: %@",controllers);
controllers = nil;

[navController pushViewController:searchViewController animated: NO];
</ code>
1
mattlangtree

モノタッチ/ xamarin IOSの場合:

uISplitViewControllerクラス内。

UINavigationController mainNav = this._navController; 
//List<UIViewController> controllers = mainNav.ViewControllers.ToList();
mainNav.ViewControllers = new UIViewController[] { }; 
mainNav.PushViewController(detail, true);//to have the animation
0
Nabil.A

正確な答えではありませんが、いくつかのシナリオで役立つかもしれません(たとえば私の場合):

ViewController Cをポップして、A(Cの下にあるもの)ではなくB(スタック外)に移動する必要がある場合、Cの前にBをプッシュし、3つすべてをスタックに置くことができます。 Bプッシュを非表示にして、Cのみをポップするか、CとBを完全にポップするかを選択することにより、同じ効果を実現できます。

最初の問題A-> C(スタックからCをポップしてBを表示したい)

可能な解決策A-> B(非表示のプッシュ)-> C(Cをポップするときに、Bを表示するか、ポップすることも選択します)

0
alasker

Navigation View Controllerの配列を使用して、Navigation Stackに追加したすべてのView Controllerを確認できます。その配列を使用することにより、特定のView Controllerに戻ることができます。

0
jignesh

このソリューションを使用して、アニメーションを保持します。

[self.navigationController pushViewController:controller animated:YES];
NSMutableArray *newControllers = [NSMutableArray arrayWithArray:self.navigationController.viewControllers];
[newControllers removeObject:newControllers[newControllers.count - 2]];
[self.navigationController setViewControllers:newControllers];
0
code4j

あるいは、

categoryを使用して、self.navigationControllernilの後にpopViewControllerAnimatedになる

ポップしてプッシュするだけで、理解しやすく、viewControllersにアクセスする必要はありません...

// UINavigationController+Helper.h
@interface UINavigationController (Helper)

- (UIViewController*) popThenPushViewController:(UIViewController *)viewController animated:(BOOL)animated;

@end


// UINavigationController+Helper.m
@implementation UINavigationController (Helper)

- (UIViewController*) popThenPushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    UIViewController *v =[self popViewControllerAnimated:NO];

    [self pushViewController:viewController animated:animated];

    return v;
}
@end

あなたのViewControllerで

// #import "UINavigationController+Helper.h"
// invoke in your code
UIViewController *v= [[MyNewViewController alloc] init];

[self.navigationController popThenPushViewController:v animated:YES];

RELEASE_SAFELY(v);
0
payliu