web-dev-qa-db-ja.com

iOS:popViewControllerの予期しない動作

インターネットで解決策を探しています。私が見つけることができるものは何もありません。だから:私はUINavigationControllerを使用しています。 2つのUIViewControllersをプッシュします。 2番目にプッシュされたViewControllerでは、このコードを実行しています:

- (void)reverseGeocoder:(MKReverseGeocoder *)geocoder didFailWithError:(NSError *)error {
NSLog([error localizedDescription]);
[self.navigationController popViewControllerAnimated:YES]; }

予想されることは、最後にプッシュされたViewControllerが消えることです。このアプリでは、いくつかの場所でこれを行っていますが、この非常にViewControllerで期待されるすべての場所で正常に動作します。何が起こるかは、戻るボタンだけが画面から消える(アニメーション化される)が、他のすべては画面に残るということです。コンソール出力では、この行を実行すると2つのことが出力されます。

2011-03-14 16:32:44.580 TheAppXY [18518:207]ネストされたポップアニメーションにより、ナビゲーションバーが破損する可能性があります

2011-03-14 16:32:53.507 TheAppXY [18518:207]予期しない状態でナビゲーション遷移を終了します。ナビゲーションバーのサブビューツリーが破損する場合があります。

情報が見つからない2つのエラーメッセージ。 XCode 4とiOS SDK 4.3を使用しています。たぶん誰もがこの問題で私を助けることができます。

49
Christoph

私は私のコードで同様の状況に遭遇し、メッセージは言った:

ネストされたプッシュアニメーションにより、ナビゲーションバーが破損する場合がある

予期しない状態でのナビゲーション遷移の仕上げ。ナビゲーションバーのサブビューツリーが破損する可能性があります。

この問題に対する私の発見は、2つのView Controllerをすばやく連続してプッシュし、両方がアニメーション化されたことです。

あなたの場合、複数のView Controllerをアニメーションで次々とポップしているようです。

したがって、あるビューがアニメーションを実行している間は、別のビューでアニメーションを開始しないでください。

また、1つのビューでアニメーションを無効にすると、エラーメッセージが消えることもわかりました。

私の場合、2つのView Controllerを次々にプッシュするつもりはなかったため、制御ロジックに問題がありました。 1つはスイッチケースロジック内にプッシュされ、もう1つは終了後にプッシュされていました。

これが誰かを助けることを願っています。

50
Vishal Chaudhry

これは、viewDidAppearの前にポップしようとするといつでも取得できます。フラグを設定した場合、viewDidAppearでそのフラグをチェックするだけで、問題は発生しません。

28
Grady Player

UINavigationControllerのドロップイン置換を作成しました。これはアニメーションをキューに入れ、この問題を完全に回避します。

BufferedNavigationController から取得します

12
Andrew

私もこの問題を抱えていましたが、これが私の原因でした:

  1. RootViewControllerでは、いくつかのUISegmentedControlオブジェクトを使用して、次に読み込む多くのビューを決定します。
  2. その(サブ/ 2番目の)ビューで、(「戻る」ボタンを使用して)RootViewControllerに戻りました。
  3. RootViewControllerでは、viewWillAppearを処理して、各UISegmentedControlオブジェクトをselectedSegmentIndex -1に「リセット」していました(つまり、「押された」セグメントはありません)。
  4. その「リセット」により、各UISegmentedControlオブジェクトがトリガーされ、関連する(および別個の)IBActionが起動されます。
  5. -1の「選択」を処理していなかったため、複数のメソッドを同時に起動し、すべてが異なるビューをプッシュしようとしました。

私の修正? if ... thenステートメントを強化し、selectedSegmentIndex == -1のときにUISegmentedControl IBActionsのコードを実行することで保釈しました。

「プッシュ」エラーではなく「ポップ」アニメーションエラーが発生した理由はまだわかりませんが、少なくとも私のエラーを把握して修正しました。

これが他の人の助けになることを願っています!

3
mbm29414

ストーリーボードを使用すると、この問題が発生します。間違いを犯しました。SegueWithIdentifierを実行するアクションを持つUIButtonがあります。このため、プッシュセグエをボタンで他のViewControllerとリンクしているため、この問題が発生します。

解決するには:UIButtonでボタンアクションをリンクし、2つのViewController間でプッシュセグエをリンクします。

0

MilGraとAndrewの回答を組み合わせることで、確実に機能し、ドロップイン式のUINavigationControllerをより簡単に置き換えることができました。

これにより、MilGraの答えが改善され、プッシュとポップで動作するようになりましたが、AndrewのBufferedNavigationControllerよりも簡単です。 (BufferedNavigationControllerを使用すると、ときどき完了しないトランジションが発生し、黒い画面しか表示されませんでした。)

このすべてはiOS8では必要ないようですが、iOS7ではまだ必要でした。

@implementation UINavigationControllerWithQueue {
    NSMutableArray *waitingList;
}

-(void) viewDidLoad {
    [super viewDidLoad];
    self.delegate = self; // NOTE: delegate must be self!
    waitingList = [[NSMutableArray alloc] init];
}

# pragma mark - Overrides

-(void) pushViewController: (UIViewController*) controller
                  animated: (BOOL) animated {
    [self queueTransition:^{ [super pushViewController:controller animated:animated]; }];
}

- (UIViewController *)popViewControllerAnimated:(BOOL)animated {
    UIViewController *result = [self.viewControllers lastObject];
    [self queueTransition:^{ [super popViewControllerAnimated:animated]; }];
    return result;
}

- (NSArray*)popToRootViewControllerAnimated:(BOOL)animated {
    NSArray* results = [self.viewControllers copy];
    [self queueTransition:^{ [super popToRootViewControllerAnimated:animated]; }];
    return results;
}

# pragma mark - UINavigationControllerDelegate

-(void) navigationController: (UINavigationController*) navigationController
       didShowViewController: (UIViewController*) controller
                    animated: (BOOL) animated {
    [self dequeTransition];
}

# pragma mark - Private Methods

-(void) queueTransition:(void (^)()) transition {
    [waitingList addObject:transition];
    if (waitingList.count == 1) {
        transition();
    }
}

-(void) dequeTransition {
    if (waitingList.count > 0) {
        [waitingList removeObjectAtIndex:0];
    }
    if (waitingList.count > 0) {
        void (^transition)(void) = [waitingList objectAtIndex:0];
        if (transition) {
            transition();
        }
    }
}

@end
0
MarkWPiper

ええ、残念ながらAppleはUINavigationControllerのアニメーションを同期しませんでした。Andrewのソリューションは優れていますが、機能全体をカバーしたくない場合は、次の2つのメソッドをオーバーライドします。

// navigation end event

- ( void )  navigationController    : ( UINavigationController* ) pNavigationController 
            didShowViewController   : ( UIViewController*       ) pController 
            animated                : ( BOOL                    ) pAnimated
{

    if ( [ waitingList count ] > 0 ) [ waitingList removeObjectAtIndex : 0 ];
    if ( [ waitingList count ] > 0 ) [ super pushViewController : [ waitingList objectAtIndex : 0 ] animated : YES ];

}


- ( void )  pushViewController  : ( UIViewController* ) pController 
            animated            : ( BOOL ) pAnimated
{

    [ waitingList addObject : pController ];
    if ( [ waitingList count ] == 1 ) [ super pushViewController : [ waitingList objectAtIndex : 0 ] animated : YES ];

}

そして、waitingListというNSMutableArrayインスタンス変数を作成すれば完了です。

0
MilGra