web-dev-qa-db-ja.com

ルートビューコントローラーにポップバックしてから、別のビューにプッシュする方法は?

3つのView Controllerを備えたシンプルなアプリケーションを書いています。ルートビューコントローラーはitem listing、基本的なテーブルビュー。このビューコントローラーから、ユーザーの操作に基づいて2つの異なるビューコントローラーをプッシュします-create itemビューコントローラまたはview itemビューコントローラ。

したがって、ストーリーボードセグエはVのように見えます。

私の〜の上に create itemビューコントローラー、ユーザーが新しいアイテムを作成したときにルートビューコントローラーにポップバックし、view itemコントローラ。新しく作成したアイテムを確認できます。

これが機能しないようです。ルートビューコントローラーに戻るのは簡単ですが、それをプッシュすることはできませんview itemコントローラ。

何か案は?以下にコードを貼り付けました。 pop関数は機能しますが、新しいビューは表示されません。

- (void) onSave:(id)sender {

    CLLocation *currentLocation = [[LocationHelper sharedInstance] currentLocation];

    // format the thread object dictionary
    NSArray* location = @[ @(currentLocation.coordinate.latitude), @(currentLocation.coordinate.longitude) ];
    NSDictionary* thread = @{ @"title": _titleField.text, @"text": _textField.text, @"author": @"mustached-bear", @"location": location };

    // send the new thread to the api server
    [[DerpHipsterAPIClient sharedClient] postPath:@"/api/thread"
                                       parameters:thread
                                          success:^(AFHTTPRequestOperation *operation, id responseObject) {

                                              // init thread object
                                              Thread *thread = [[Thread alloc] initWithDictionary:responseObject];

                                              // init view thread controller
                                              ThreadViewController *viewThreadController = [[ThreadViewController alloc] init];
                                              viewThreadController.thread = thread;

                                              [self.navigationController popToRootViewControllerAnimated:NO];
                                              [self.navigationController pushViewController:viewThreadController animated:YES];

                                          }
                                          failure:^(AFHTTPRequestOperation *operation, NSError *error) {

                                              [self.navigationController popToRootViewControllerAnimated:YES];

                                          }];

}
16
Ryan

やりたいことを簡単に行う方法は、いくつかの単純なロジックをメインのルートビューコントローラー-(void)viewWillAppearメソッドに組み込み、デリゲートコールバックを使用してロジックスイッチを切り替えることです。基本的には、ルートコントローラへの「後方参照」です。これは簡単な例です。

メインルートコントローラー(このコントローラーaを検討してください)-コントローラーという名前を付けて、ジャンプステータスを追跡するプロパティを設定します

@property (nonatomic) BOOL jumpNeeded;

ロジックをセットアップする

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    self.jumpNeeded ? NSLog(@"jump needed") : NSLog(@"no jump needed");

    if (self.jumpNeeded) {
        NSLog(@"jumping");
        self.jumpNeeded = NO;
        [self performSegueWithIdentifier:@"controllerC" sender:self];
    }   
}

今、あなたのメインルートコントローラーでは、テーブルビューの行が選択されているときに、テーブルビューのcontrollerBにプッシュするときに次のようなことを行いますselectメソッド

[self performSegueWithIdentifer@"controllerB" sender:self];

次に、segueメソッドの準備を実装します

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {

  //setup controller B
  if([segue.identifier isEqualTo:@"controllerB"]){
    ControllerB *b = segue.destinationViewController;
    b.delegate = self;  //note this is the back reference
  }

  //implement controller c here if needed
}

次に、controllerBに移動します。「delegate」というプロパティを設定して、後方参照を保持し、ヘッダーファイルをルートコントローラーからインポートする必要があります。

#import "controllerA"

@property (nonatomic,weak) controllerA *delegate;

その後、controllerAに戻る直前に、フラグを設定します

   self.delegate.jumpNeeded = YES;
    [self.navigationController popViewControllerAnimated:YES];

そしてそれはそれについてです。 controllerCで何もする必要はありません。他にもいくつかの方法がありますが、これはあなたのニーズにとってかなり簡単です。それがあなたのためにうまくいくことを願っています。

6
CocoaEv

私があなたを正しく理解していれば、View Controllerのスタックがあります。

A (root) - B - C - D - E

そして、あなたはそれがなりたいと思っています:

A (root) - F

正しい?その場合:

NSArray *viewControllers = self.navigationController.viewControllers;
NSMutableArray *newViewControllers = [NSMutableArray array];

// preserve the root view controller
[newViewControllers addObject:[viewControllers objectAtIndex:0]];
// add the new view controller
[newViewControllers addObject:viewThreadController];
// animatedly change the navigation stack
[self.navigationController setViewControllers:newViewControllers animated:YES];

Swift 4

// get current view controllers in stack and replace them
let viewControllers = self.navigationController!.viewControllers
let newViewControllers = NSMutableArray()

// preserve the root view controller
newViewControllers.add(viewControllers[0])
// add the new view controller
newViewControllers.add(viewThreadController)
// animatedly change the navigation stack
self.navigationController?.setViewControllers(newViewControllers as! [UIViewController], animated: true)
40
Dave DeLong

おもう
[self.navigationController pushViewController:viewThreadController animated:YES];は、その前のステートメントとは異なるNavigationControllerを使用しています。ルートビューコントローラーにポップした後、ナビゲーションコントローラーが失われるためです。代わりにこのコードを使用して解決してください。

UINavigationController *nav = self.navigationController; 
[self.navigationController popToRootViewControllerAnimated:NO];
[nav pushViewController:viewThreadController animated:YES];

これはあなたの問題全体を解決しないと私は思います。おそらく、2つの高速ポップとプッシュがNavigationControllerを無効にする可能性があるというエラーが表示されます。
そしてそれを解決するには、2番目のビューコントローラーのviewDidDissappearメソッドでNavigationControllerをプッシュするか、メインビューコントローラー(アイテムリスト)のviewDidAppearメソッドでプッシュします。

8
M.C.

アプリケーションで使用するDave DeLongの回答に基づいてUINavigationControllerでカテゴリを共有し、必要に応じて常に戻るボタンを機能させます。

@implementation UINavigationController (PushNewAndClearNavigationStackToParent)

- (void) pushNewControllerAndClearStackToParent:(UIViewController*)newCont animated:(BOOL)animated
{
    NSArray *viewControllers = self.viewControllers;
    NSMutableArray *newViewControllers = [NSMutableArray array];
    
    // preserve the root view controller
    [newViewControllers addObject:[viewControllers firstObject]];
    // add the new view controller
    [newViewControllers addObject:newCont];
    // animatedly change the navigation stack
    [self setViewControllers:newViewControllers animated:animated];
}

@end
5
jlichti
NSArray *viewControllers = self.navigationController.viewControllers;
NSMutableArray *newViewControllers = [NSMutableArray array];

// preserve the root view controller
for(int i = 0; i < [viewControllers count];i++){
    if(i != [viewControllers count]-1)
        [newViewControllers addObject:[viewControllers objectAtIndex:i]];
}
// add the new view controller
[newViewControllers addObject:conversationViewController];
// animatedly change the navigation stack
[self.navigationController setViewControllers:newViewControllers animated:YES];
1
user5018497

私はデイブの答えをインスピレーションとして使用し、これをスウィフトのために作成しました:

let newViewControllers = NSMutableArray()
newViewControllers.addObject(HomeViewController())
newViewControllers.addObject(GroupViewController())
let swiftArray = NSArray(array:newViewControllers) as! Array<UIViewController>
self.navigationController?.setViewControllers(swiftArray, animated: true)
  1. HomeViewController()をボトムビューコントローラーにしたいものに置き換えます
  2. GroupViewController()を、トップビューコントローラにしたいものに置き換えます。
0

同じナビゲーションコントローラ内で何らかの方法でナビゲートしたい場合は、navigationStack A.K.Aにアクセスできます。 viewControllersを実行してから、viewcontrollersを必要な順序で設定するか、いくつかを追加または削除します...これは、最もクリーンでネイティブな方法です。

        UINavigationController* theNav = viewControllerToDismiss.navigationController;
        UIViewController* theNewController = [UIViewController new];
        UIViewController* rootView = theNav.viewControllers[0];
        [theNav setViewControllers:@[
                                     rootView,
                                     theNewController
                                     ] animated:YES];

必要な検証を行って、例外が発生しないようにします。

0
user2387149

ポップバックするとすべての割り当てが解除されるため、これは可能ではないと思います。

より良い方法は、データをモデルシングルトンクラスに配置し、rootviewcontrollerにポップして、ポップが終了するまでリッスンすることです。次に、モデルにデータが保存されているかどうかを確認して、新しいビューコントローラーをプッシュする必要があるかどうかを確認します。

0
polyclick