<UITabBarController:0x197870>の外観遷移を開始/終了するための不均衡な呼び出し


118

同様のエラー発生した別のユーザーについてSOを読みましたが、このエラーは別のケースにあります。

最初にView Controllerを追加したときにこのメッセージを受け取りました。

Unbalanced calls to begin/end appearance transitions for 
<UITabBarController: 0x197870>

アプリの構造は次のとおりです。

5つのView Controllerにリンクされた5つのタブTabBarControllerを取得しました。最初に表示されるタブで、新しいView Controllerを呼び出して、アプリの紹介としてオーバーレイします。

このコードを使用して、Introduction View Controllerを呼び出します。

IntroVC *vc = [[IntroVC alloc] init];
[self presentModalViewController:vc animated:YES];
[vc release]; 

このIntroVCView Controllerが表示された後、上記のエラーが表示されます。

PS私はxCode 4.2とiOS 5.0 SDKを使用して、iOS 4.3アプリを開発しています。


こんにちはシヴァン、私もあなたと同じ問題を抱えています。しかし、私は以下の答えを見た後でもそれを修正することはできません。イントロダクションビューコントローラーの呼び出し場所を教えてください。
ZYiOS

回答:


98

周囲のコードをもっと見ないと、明確な答えを出すことはできませんが、2つの理論があります。

  1. UIViewController指定された初期化子initWithNibName:bundle:を使用していません。だけではなく、それを使用してみてくださいinit

  2. また、selfタブバーコントローラーのビューコントローラーの1つである場合もあります。常に最上位のビューコントローラーからビューコントローラーを提示します。つまり、この場合、タブコントローラーにビューコントローラーの代わりにオーバーレイビューコントローラーを提示するよう依頼します。コールバックデリゲートを実際のビューコントローラーに保持することはできますが、タブバーコントローラーを存在させて閉じる必要があります。


2
#1はこの問題を解決しました。initの代わりにinitWithNibName:nil bundle:nilを使用しました。
Hua-Ying、

172
アプリが初期化を完了する前にモーダルvcを提示することにより、この警告を生成できます。つまり、タブ付きのアプリケーションテンプレートアプリを起動し、application:didFinishLaunchingの最後の行としてself.tabBarControllerの上にモーダルvcを表示します。警告が表示されます。解決策:最初にスタックを巻き戻し、modal vcを、performSelector withDelay:0.0で呼び出される別のメソッドで提示します。
danh

9
そして、もう1つの質問は、performSelector withDelayが機能する理由を説明しています。stackoverflow.com/questions/1922517/...
ファティ

1
danhの解決策はうまくいきましたが、0.0ではなく0.1を使用する必要がありました。
Brandon O'Rourke

11
ゼロのperformSelectorWithDelayを使用するのではなく、viewDidLoadなどではなくviewDidAppearでこれを実行してください。
tooluser 2014年

40

アニメーションをYESからNOに変更して、このエラーを修正しました。

から:

[tabBarController presentModalViewController:viewController animated:YES];

に:

[tabBarController presentModalViewController:viewController animated:NO];

4
これはアニメーションを気にしない場合の問題を修正しますが、animationed:YESが必要な場合は、受け入れられた回答についてdanhのコメントを試してください。 stackoverflow.com/questions/7886096/...
wxactly

3
参考:presentModalViewController:animated:はiOS6で廃止されました。
ZS

16

danhによる投稿

アプリの初期化が完了する前にモーダルvcを提示することで、この警告を生成できます。つまり、タブ付きアプリケーションテンプレートアプリを起動し、self:tabBarControllerの上にモーダルvcをapplication:didFinishLaunchingの最後の行として表示します。警告が表示されます。解決策:最初にスタックをほどかせ、performSelector withDelay:0.0で呼び出された別のメソッドでモーダルvcを提示します。

メソッドをviewWillAppearに移動してガードし、一度だけ実行されるようにします(プロパティの設定をお勧めします)。


なぜviewWillAppearありませんかviewDidAppear
Cyber​​Mew

6

多くの場合の別の解決策は、次のようにして、不適切な(初期化中など)プロシージャが終了した後にUIViewController s 間の遷移が発生するようにすることです。

__weak MyViewController *weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
    [weakSelf presentViewController:vc animated:YES];
});

これはpushViewController:animated:、などにも一般的です。


4

私も同じ問題を抱えていました。viewDidLoad最初に内部でメソッドを呼び出しましたUIViewController

- (void)viewDidLoad{
    [super viewDidLoad];

    [self performSelector:@selector(loadingView)
               withObject:nil afterDelay:0.5];
}

- (void)loadingView{

    [self performSegueWithIdentifier:@"loadedData" sender:self];
}

第二内部UIViewControllerI 0.5秒の遅延でも同じことをやりました。遅延をより高い値に変更すると、問題なく動作しました。それはセグエが次のセグエの後にあまり速く実行できないようなものです。


7
ビューのライフサイクルメソッドviewDidAppearは、まさにこの目的のために提供されており、人為的な遅延fwiwを導入するよりも信頼性が高くなります。
tooluser 2014年

1
これは正解です。ただし、遅延0は、ナビゲーションコントローラーが新しいナビゲーションの準備ができるまで待機するのに十分です。
マルハル2014

それは完全に正しいです、あなたはそれを処理する準備ができているviewDidAppearように内部でそれを呼ばなければなりませんUINavigationController。投稿をこれに変更しました;)
Alex Cio

これをviewWillAppearに移動する必要があるように思えます。そうすれば、ビューが初期化されているかどうかについて心配する必要はありません。
horsejockey 2015

3

別のビューコントローラーからログインビューコントローラーを提示する必要があるときに同じ問題が発生しました ViewDidAppearメソッドで作成を開始したときに、この問題を解決しました。ViewDidLoadはプロパティを初期化するだけだと思います。その後、実際の表示ビューアルゴリズムが始まります。そのため、モーダル遷移を行うにはviewDidAppearメソッドを使用する必要があります。


3

タイプミスが原因でこの問題が発生しました:

override func viewDidAppear(animated: Bool) {
    super.viewWillAppear(animated)

の代わりに

override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)

スーパーで「DidAppear」ではなく「WillAppear」を呼び出していました


2

同じ問題で多くの問題がありました。私はこれを解決しました

  1. storyboad instantiateViewControllerWithIdentifierメソッドを使用してViewControllerを開始します。すなわちIntro *vc = [self.storyboard instantiateViewControllerWithIdentifier:@"introVC"];
  2. [self.tabBarController presentModalViewController : vc animated:YES];

ストーリーボードにビューコントローラーがありますが、何らかの理由でを使用するだけで[[introvc alloc] init];はうまくいきませんでした。


1
新しいストーリーボード機能を使用してお会いできてうれしいです。しかし、私の場合はストーリーボードを使用していませんでした...
Raptor

「instantiateViewControllerWithIdentifier」がコントローラの識別子をとることを指摘したかっただけです。詳細については、stackoverflow.com
questions / 8186375

2

私はそれを書いて解決しました

[self.navigationController presentViewController:viewController 
                                        animated:TRUE 
                                      completion:NULL];

3
FYIあなたが行う必要があり、より慣用的な(!、より安全な)されるように:アニメーション:YES完了:ゼロ
powerj1984

2
もっと慣用的にしますが、どのように安全ですか?
Zev Eisenberg 2014

2

サードパーティのコードでこの問題が発生しました。誰かがカスタムのTabBarControllerクラスでviewWillAppearとviewWillDisappearのスーパーを設定するのを忘れていました。

- (void) viewWillAppear:(BOOL)animated {

    [super viewWillAppear:animated];
    // code...
}

or

- (void) viewWillDisappear:(BOOL)animated {

    [super viewWillDisappear:animated];
    // code...
}

2

transitioningDelegate(この質問の例ではそうではない)を使用している場合は、次のように設定modalPresentationStyleします。.Customます。

迅速

let vc = storyboard.instantiateViewControllerWithIdentifier("...")
vc.transitioningDelegate = self
vc.modalPresentationStyle = .Custom

1

同じエラーが発生しました。3つのアイテムを含むタブバーがあり、タブバーのアイテム2でアイテム1のルートビューコントローラーを無意識に呼び出そうとしていましたperformSegueWithIdentifier

何が起こるかというと、それがビューコントローラーを呼び出し、数秒後にアイテム2のルートビューコントローラーに戻り、そのエラーをログに記録することです。

どうやら、アイテムのルートビューコントローラーを別のアイテムに呼び出すことはできません。

だから代わりに performSegueWithIdentifier

使った [self.parentViewController.tabBarController setSelectedIndex:0];

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


1

私は同じ問題を抱えていて、誰かが似たようなものに遭遇した場合に備えて投稿すると思いました。

私の場合、UITableViewControllerに長押しジェスチャーレコグナイザーを接続しました。

UILongPressGestureRecognizer *longPressGesture = [[[UILongPressGestureRecognizer alloc]
                                                   initWithTarget:self
                                                   action:@selector(onLongPress:)]
                                                  autorelease];
[longPressGesture setMinimumPressDuration:1];
[self.tableView addGestureRecognizer:longPressGesture];

私のonLongPressセレクターで、次のビューコントローラーを起動しました。

- (IBAction)onLongPress:(id)sender {

    SomeViewController* page = [[SomeViewController alloc] initWithNibName:@"SomeViewController" bundle:nil];

    [self.navigationController pushViewController:page animated:YES];

    [page release];

}

私の場合、長押し認識機能が複数回起動され、その結果、「SomeViewController」がスタックに複数回プッシュされたため、エラーメッセージが表示されました。

解決策は、SomeViewControllerがいつスタックにプッシュされたかを示すブール値を追加することでした。UITableViewControllerのviewWillAppearメソッドが呼び出されたときに、ブール値をNOに戻しました。


1

ストーリーボードを使用している場合は、新しいビューコントローラを表示するコードをviewDidAppearに配置する必要があることがわかりました。また、「切り離されたビューコントローラーでのビューコントローラーの表示は推奨されません」という警告が表示されなくなります。


1

ではスウィフト2+私の作品:

ストーリーボードにUITabBarViewControllerがあり、次のようなselectedIndexプロパティがありました。

ここに画像の説明を入力してください

しかし、それを削除して、次のように、最初のクラスのviewDidLoadメソッドに追加します。

override func viewDidLoad() {
   super.viewDidLoad()
   self.tabBarController?.selectedIndex = 2
}

私は誰かを助けることができると思います。


0

実際には、プッシュアニメーションが終了するまで待つ必要があります。したがって、UINavigationControllerを委任して、アニメーションが終了するまでプッシュされないようにすることができます。

- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated{
    waitNavigation = NO;
}


-(void)showGScreen:(id)gvc{

    if (!waitNavigation) {
        waitNavigation = YES;
        [_nav popToRootViewControllerAnimated:NO];
        [_nav pushViewController:gvc animated:YES];
    }
}

セルが選択されたときに呼び出します。それはあなた次第です
ymutlu 2014

0

@danhが示唆したように、私の問題は、UITabBarController準備が整う前にモーダルvcを提示していたことでした。ただし、ビューコントローラーを表示する前に、固定遅延に依存することに不快に感じました(私のテストから、で0.05〜0.1秒の遅延を使用する必要がありましたperformSelector:withDelay:)。私の解決策はUITabBarControllerviewDidAppear:メソッドで呼び出されるブロックを追加することです:

PRTabBarController.h:

@interface PRTabBarController : UITabBarController

@property (nonatomic, copy) void (^viewDidAppearBlock)(BOOL animated);

@end

PRTabBarController.m:

#import "PRTabBarController.h"

@implementation PRTabBarController

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    if (self.viewDidAppearBlock) {
        self.viewDidAppearBlock(animated);
    }
}

@end

application:didFinishLaunchingWithOptions:

PRTabBarController *tabBarController = [[PRTabBarController alloc] init];

// UIWindow initialization, etc.

__weak typeof(tabBarController) weakTabBarController = tabBarController;
tabBarController.viewDidAppearBlock = ^(BOOL animated) {
    MyViewController *viewController = [MyViewController new];
    viewController.modalPresentationStyle = UIModalPresentationOverFullScreen;
    UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:viewController];
    [weakTabBarController.tabBarController presentViewController:navigationController animated:NO completion:nil];
    weakTabBarController.viewDidAppearBlock = nil;
};

0

-(void)beginAppearanceTransition:(BOOL)isAppearing animated:(BOOL)animatedおよび-(void)endAppearanceTransitionがクラスで一緒に作成されていることを確認する必要があります。


0

同じ問題がありました。開発中、画面をバイパスしたかった。セレクターメソッドを呼び出して、viewDidLoadでビューコントローラー間を移動していました。

問題は、別のViewControllerに移行する前に、ViewControllerの移行を完了する必要があることです。

これで私の問題は解決しました。遅延は、ViewControllerが別のトランジションに移行する前にトランジションを完了するために必要です。

self.perform(#selector(YOUR SELECTOR METHOD), with: self, afterDelay: 0.5)

0

スウィフト5

 func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {


//Delete or comment the below lines on your SceneDelegate.

//        guard let windowScene = (scene as? UIWindowScene) else { return }
//        window?.windowScene = windowScene
//        window?.makeKeyAndVisible()

        let viewController = ListVC()
        let navViewController = UINavigationController(rootViewController: viewController)
        window?.rootViewController = navViewController

    }

-1

ルートTVCからTVC Aに、次にTVC Bにナビゲートしたときに、この問題が発生しました。TVCBIの「ロード」ボタンをタップした後、ルートTVCに直接ジャンプしたいと思った(TVC Aに再度アクセスする必要がないので、なぜそうするのか) 。私が持っていた:

//Pop child from the nav controller
[self.navigationController popViewControllerAnimated:YES];
//Pop self to return to root
[self.navigationController popViewControllerAnimated:YES];

...「開始/終了などの呼び出しのバランスが取れていません」というエラーが発生しました。以下はエラーを修正しましたが、アニメーションはありません:

//Pop child from the nav controller
[self.navigationController popViewControllerAnimated:NO];
//Then pop self to return to root
[self.navigationController popViewControllerAnimated:NO];

これは私の最終的な解決策であり、エラーはなく、まだアニメーション化されていました:

//Pop child from the nav controller
[self.navigationController popViewControllerAnimated:NO];
//Then pop self to return to root, only works if first pop above is *not* animated
[self.navigationController popViewControllerAnimated:YES];

-1

UIButtonをストーリーボードセグエアクション(IB内)にフックしたときにこのエラーが発生しましたが、後でプログラムによってボタンを呼び出すことにしました performSegueWithIdentifier IBから最初の1つを削除するのを忘れてことにしました。

本質的には、セグエ呼び出しを2回実行し、このエラーを出し、実際に私の見方を2回押しました。修正は、セグエ呼び出しの1つを削除することでした。

これが私と同じくらい疲れている人に役立つことを願っています!

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.