Unausgeglichene Aufrufe zum Beginnen / Beenden von Erscheinungsübergängen für <UITabBarController: 0x197870>

118

Ich habe SO über einen anderen Benutzer gelesen, bei dem ein ähnlicher Fehler aufgetreten ist , aber dieser Fehler tritt in einem anderen Fall auf.

Ich habe diese Nachricht erhalten, als ich anfänglich einen View Controller hinzugefügt habe:

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

Die Struktur der App ist wie folgt:

Ich habe einen TabBarController mit 5 Registerkarten, der mit 5 View Controllern verknüpft ist. Auf der Registerkarte "Erste Anzeige" rufe ich einen neuen View Controller zum Überlagern als Einführung in die App auf.

Ich benutze diesen Code, um den Einführungsansichts-Controller aufzurufen:

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

Nachdem dieser IntroVCAnsichts-Controller angezeigt wurde, wird der obige Fehler angezeigt.

ps Ich verwende xCode 4.2 und iOS 5.0 SDK und entwickle die iOS 4.3 App.

Raubvogel
quelle
Hallo Shivan, ich habe das gleiche Problem mit dir. Aber ich kann es immer noch nicht beheben, nachdem ich die Antworten unten angezeigt habe. Darf ich wissen, wo Sie den Controller für die Einführungsansicht anrufen?
ZYiOS

Antworten:

98

Ohne mehr von dem umgebenden Code zu sehen, kann ich keine eindeutige Antwort geben, aber ich habe zwei Theorien.

  1. Sie verwenden nicht UIViewControllerden vorgesehenen InitialisiererinitWithNibName:bundle: . Versuchen Sie es statt nur init.

  2. Auch selfkann eine der Registerkarte Ansicht - Controller in der Bar - Controller. Präsentieren Sie View Controller immer vom obersten View Controller aus. In diesem Fall bitten Sie den Registerkarten-Controller, den Overlay View Controller im Namen des View Controllers zu präsentieren. Sie können weiterhin alle Rückrufdelegierten für den Real View Controller behalten, aber Sie müssen den Registerkarten-Controller vorhanden haben und schließen.

Jesper
quelle
2
# 1 hat dieses Problem für mich behoben. Ich habe initWithNibName: nil bundle: nil anstelle von init verwendet.
Hua-Ying
172
Sie können diese Warnung generieren, indem Sie die modale VC vor der Initialisierung der App anzeigen. dh Starten Sie eine Anwendungsvorlagen-App mit Registerkarten und präsentieren Sie eine modale VC über self.tabBarController als letzte Zeile in der Anwendung: didFinishLaunching. Warnung erscheint. Lösung: Lassen Sie den Stapel zuerst abwickeln und präsentieren Sie das modale VC in einer anderen Methode, die mit einem performSelector withDelay: 0.0 aufgerufen wird.
Danh
9
Und hier ist eine andere Frage, die erklärt, warum performSelector withDelay funktioniert. stackoverflow.com/questions/1922517/…
fatih
1
danhs lösung hat bei mir funktioniert, aber ich musste 0.1 statt 0.0 verwenden.
Brandon O'Rourke
11
Anstatt ein performSelectorWithDelay von Null zu verwenden, führen Sie dies in viewDidAppear anstelle von viewDidLoad oder so weiter aus.
Tooluser
40

Ich habe diesen Fehler behoben, indem ich die Animation von JA auf NEIN geändert habe.

Von:

[tabBarController presentModalViewController:viewController animated:YES];

Zu:

[tabBarController presentModalViewController:viewController animated:NO];
PokerIncome.com
quelle
4
Dies behebt das Problem, wenn Sie sich nicht für Animation interessieren, aber wenn Sie animiert benötigen: JA, versuchen Sie es mit Danhs Kommentar zur akzeptierten Antwort: stackoverflow.com/questions/7886096/…
genau am
3
Zu Ihrer Information: presentModalViewController: animiert: war in iOS6 veraltet.
ZS
16

Wie von danh gepostet

Sie können diese Warnung generieren, indem Sie die modale VC vor der Initialisierung der App anzeigen. dh Starten Sie eine Anwendungsvorlagen-App mit Registerkarten und präsentieren Sie eine modale VC über self.tabBarController als letzte Zeile in der Anwendung: didFinishLaunching. Warnung erscheint. Lösung: Lassen Sie den Stapel zuerst abwickeln und präsentieren Sie das modale VC in einer anderen Methode, die mit einem performSelector withDelay: 0.0 aufgerufen wird

Versuchen Sie, die Methode in viewWillAppear zu verschieben und zu schützen, damit sie nur einmal ausgeführt wird (würde empfehlen, eine Eigenschaft einzurichten).

Peter Lapisu
quelle
Warum viewWillAppearund nicht viewDidAppear?
CyberMew
6

Eine andere Lösung für viele Fälle besteht darin, sicherzustellen, dass der Übergang zwischen UIViewControllers nach Abschluss des nicht geeigneten Verfahrens (wie während der Initialisierung) erfolgt, indem Folgendes ausgeführt wird:

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

Dies gilt auch für pushViewController:animated:usw.

mllm
quelle
4

Ich hatte das gleiche Problem. Ich habe eine Methode viewDidLoadin meinem ersten aufgerufenUIViewController

- (void)viewDidLoad{
    [super viewDidLoad];

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

- (void)loadingView{

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

Innerhalb der Sekunde habe UIViewControllerich das auch mit 0,5 Sekunden Verzögerung gemacht. Nachdem die Verzögerung auf einen höheren Wert geändert wurde, funktionierte sie einwandfrei. Es ist, als ob der Segue nach einem anderen Segue nicht zu schnell durchgeführt werden kann.

Alex Cio
quelle
7
Die View-Lifecycle-Methode viewDidAppear wird genau für diesen Zweck bereitgestellt und wäre zuverlässiger als die Einführung einer künstlichen Verzögerung (fwiw).
Tooluser
1
Dies ist die richtige Antwort, außer dass eine Verzögerung von 0 ausreicht, um zu warten, bis der Navigationscontroller für eine neue Navigation bereit ist.
Malhal
Es ist völlig richtig, man muss es drinnen anrufen, viewDidAppeardamit der UINavigationControllerbereit ist, damit umzugehen. Ich habe meinen Beitrag in diesen geändert;)
Alex Cio
Ich bin der Meinung, dass dies in viewWillAppear verschoben werden sollte, dann müssen Sie sich keine Gedanken darüber machen, ob die Ansicht initialisiert wurde oder nicht.
Horsejockey
3

Ich hatte das gleiche Problem, als ich meinen Login View Controller von einem anderen View Controller aus präsentieren musste. Wenn der Benutzer nicht autorisiert ist, habe ich dies in der ViewDidLoad-Methode meines anderen View Controllers getan (falls nicht autorisiert -> presentModalViewController). Als ich anfing, es in der ViewDidAppear-Methode zu erstellen, habe ich dieses Problem gelöst. Ich denke, dass ViewDidLoad nur Eigenschaften initialisiert und danach der eigentliche Algorithmus für die Anzeige beginnt! Deshalb müssen Sie die viewDidAppear-Methode verwenden, um modale Übergänge vorzunehmen!

Tolusha
quelle
3

Ich hatte dieses Problem wegen eines Tippfehlers:

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

anstatt

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

Es wurde "WillAppear" im Super anstelle von "DidAppear" genannt.

Adriano Spadoni
quelle
2

Ich hatte viele Probleme mit dem gleichen Problem. Ich habe dieses Problem gelöst

  1. Initiieren des ViewControllers mithilfe der Storyboad-Methode instantiateViewControllerWithIdentifier. dhIntro *vc = [self.storyboard instantiateViewControllerWithIdentifier:@"introVC"];
  2. [self.tabBarController presentModalViewController : vc animated:YES];

Ich habe den Viewcontroller in meinem Storyboard, aus irgendeinem Grund hat die Verwendung von only [[introvc alloc] init];bei mir nicht funktioniert.

Mogambolal
quelle
1
Schön, dass Sie die neue Storyboarding-Funktion verwenden. aber ich habe in meinem Fall kein Storyboard verwendet ...
Raptor
Ich wollte nur darauf hinweisen, dass "instantiateViewControllerWithIdentifier" den Bezeichner des Controllers verwendet. Weitere Informationen finden Sie unter stackoverflow.com/questions/8186375/…
Kishor Kundan
2

Ich habe es durch Schreiben gelöst

[self.navigationController presentViewController:viewController 
                                        animated:TRUE 
                                      completion:NULL];
Pankesh
quelle
3
Zu
Ihrer Information
2
Ich gebe Ihnen mehr Redewendung, aber wie ist es sicherer?
Zev Eisenberg
2

Ich hatte dieses Problem mit einem Code eines Drittanbieters. Jemand hat vergessen, das Super-Inside von viewWillAppear und viewWillDisappear in einer benutzerdefinierten TabBarController-Klasse festzulegen.

- (void) viewWillAppear:(BOOL)animated {

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

or

- (void) viewWillDisappear:(BOOL)animated {

    [super viewWillDisappear:animated];
    // code...
}
J. Lopes
quelle
2

Wenn Sie verwenden transitioningDelegate(im Beispiel dieser Frage nicht der Fall), setzen Sie ebenfalls modalPresentationStyleauf .Custom.

Schnell

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

Ich hatte den gleichen Fehler. Ich habe eine Registerkartenleiste mit 3 Elementen und habe unbewusst versucht, den Root-View-Controller von Element 1 in Element 2 meiner Registerkartenleiste mit aufzurufen performSegueWithIdentifier.

Was passiert ist, dass es den View Controller aufruft und nach einigen Sekunden zum Root View Controller von Element 2 zurückkehrt und diesen Fehler protokolliert.

Anscheinend können Sie den Root-View-Controller eines Elements nicht zu einem anderen Element aufrufen.

Also statt performSegueWithIdentifier

ich benutzte [self.parentViewController.tabBarController setSelectedIndex:0];

Hoffe das hilft jemandem.

Gellie Ann
quelle
1

Ich hatte das gleiche Problem und dachte, ich würde posten, falls jemand anderes auf etwas Ähnliches stößt.

In meinem Fall hatte ich meinem UITableViewController eine lange Gestenerkennung hinzugefügt.

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

In meinem onLongPress-Selektor habe ich meinen nächsten View Controller gestartet.

- (IBAction)onLongPress:(id)sender {

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

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

    [page release];

}

In meinem Fall habe ich die Fehlermeldung erhalten, weil der Langdruckerkenner mehr als einmal ausgelöst wurde und mein "SomeViewController" daher mehrmals auf den Stapel geschoben wurde.

Die Lösung bestand darin, einen Booleschen Wert hinzuzufügen, um anzuzeigen, wann der SomeViewController auf den Stapel verschoben wurde. Als die viewWillAppear-Methode meines UITableViewController aufgerufen wurde, habe ich den Booleschen Wert auf NO zurückgesetzt.

Dale Moore
quelle
1

Ich habe festgestellt, dass Sie, wenn Sie ein Storyboard verwenden, den Code, der den neuen Ansichts-Controller darstellt, in viewDidAppear einfügen möchten. Außerdem wird die Warnung "Das Anzeigen von Ansichts-Controllern auf getrennten Ansichts-Controllern wird nicht empfohlen" entfernt.

Dan Levy
quelle
1

In Swift 2+ funktioniert für mich:

Ich habe UITabBarViewController im Storyboard und ich hatte die Eigenschaft Index wie folgt ausgewählt:

Geben Sie hier die Bildbeschreibung ein

Aber ich lösche es und füge meine viewDidLoad-Methode meiner ursprünglichen Klasse wie folgt hinzu:

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

Ich hoffe ich kann jemandem helfen.

Dasoga
quelle
0

Eigentlich müssen Sie warten, bis die Push-Animation endet. So können Sie UINavigationController delegieren und das Drücken verhindern, bis die Animation endet.

- (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
quelle
Ich nenne es, wenn eine Zelle ausgewählt wird. Es hängt eigentlich von Ihnen ab
ymutlu
0

Wie @danh vorschlug, bestand mein Problem darin, dass ich das modale VC vor dem Start präsentierte UITabBarController. Es war mir jedoch unangenehm, mich auf eine feste Verzögerung zu verlassen, bevor ich den View Controller präsentierte (nach meinen Tests musste ich eine Verzögerung von 0,05 bis 0,1 Sekunden verwenden performSelector:withDelay:). Meine Lösung besteht darin, einen Block hinzuzufügen, der für UITabBarControllerdie viewDidAppear:Methode aufgerufen wird:

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

Jetzt in 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;
};
Johnboiles
quelle
0

Sie müssen sicherstellen, dass - (void) beginAppearanceTransition: (BOOL) animiert erscheint: (BOOL) animiert und - (void) endAppearanceTransition gemeinsam in der Klasse erstellt wird.

zszen
quelle
0

Ich hatte das gleiche Problem. Bei der Entwicklung wollte ich Bildschirme umgehen. Ich habe in viewDidLoad von einem Ansichts-Controller zu einem anderen navigiert, indem ich eine Auswahlmethode aufgerufen habe.

Das Problem ist, dass wir den ViewController den Übergang beenden lassen sollten, bevor wir zu einem anderen ViewController wechseln.

Dies löste mein Problem: Die Verzögerung ist erforderlich, damit ViewController den Übergang beenden können, bevor sie zu einem anderen wechseln.

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

Swift 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

    }
Marlhex
quelle
-1

Ich hatte dieses Problem, als ich von Root-TVC zu TVC A und dann zu TVC B navigiert war. Nachdem ich in TVC BI auf die Schaltfläche "Laden" geklickt hatte, wollte ich direkt zum Root-TVC zurückspringen (TVC A muss nicht erneut besucht werden. Warum also?) . Ich hatte:

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

... was den Fehler "Unausgeglichene Anrufe zum Starten / Beenden usw." ergab. Folgendes hat den Fehler behoben, aber keine Animation:

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

Dies war meine endgültige Lösung, kein Fehler und immer noch animiert:

//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];
dawid
quelle
-1

Ich bin auf diesen Fehler gestoßen, als ich einen UIButton an eine Storyboard-Segue-Aktion (in IB) angeschlossen habe, aber später beschlossen habe, die Schaltfläche programmgesteuert aufzurufen performSegueWithIdentifier zu lassen, um zu vergessen, den ersten aus IB zu entfernen.

Im Wesentlichen hat es den Segue-Aufruf zweimal ausgeführt, diesen Fehler ausgegeben und meine Ansicht tatsächlich zweimal verschoben. Die Lösung bestand darin, einen der Segue-Aufrufe zu entfernen.

Hoffe das hilft jemandem so müde wie mir!

Capikaw
quelle