Chiamate sbilanciate per iniziare / terminare le transizioni di presenza per <UITabBarController: 0x197870>


119

Ho letto SO su un altro utente che ha riscontrato un errore simile , ma questo errore è in un caso diverso.

Ho ricevuto questo messaggio quando ho aggiunto inizialmente un controller di visualizzazione:

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

La struttura dell'app è la seguente:

Ho un TabBarController a 5 schede collegato a 5 controller di visualizzazione. Nella scheda di visualizzazione iniziale, chiamo un nuovo controller di visualizzazione da sovrapporre come introduzione all'app.

Uso questo codice per chiamare il controller della vista introduttiva:

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

Dopodichè IntroVC controller di visualizzazione viene visualizzato, viene visualizzato l'errore precedente.

ps Sto usando xCode 4.2 e iOS 5.0 SDK, sviluppando l'app iOS 4.3.


Ciao Shivan, ho lo stesso problema con te. Ma non riesco ancora a risolverlo dopo aver visto sotto le risposte. Posso sapere dove chiami il controller della vista introduttiva?
ZYiOS

Risposte:


98

Senza vedere più del codice circostante non posso dare una risposta definitiva, ma ho due teorie.

  1. Non stai utilizzando UIViewControllerl' inizializzatore designato diinitWithNibName:bundle: . Prova a usarlo invece di solo init.

  2. Inoltre, selfpuò essere uno dei controller di visualizzazione del controller della barra delle schede. Presenta sempre i controller di visualizzazione dal controller di visualizzazione più in alto, il che significa che in questo caso chiedi al controller della barra delle schede di presentare il controller di visualizzazione overlay per conto del controller di visualizzazione. È comunque possibile mantenere qualsiasi delegato di richiamata al controller di visualizzazione reale, ma è necessario che il controller della barra delle schede sia presente e ignorato.


2
# 1 ha risolto questo problema per me, ho usato initWithNibName: nil bundle: nil invece di init.
Hua-Ying

172
È possibile generare questo avviso presentando la vc modale prima che l'app venga inizializzata. ad esempio, avviare un'app modello di applicazione a schede e presentare una vc modale sopra self.tabBarController come ultima riga dell'applicazione: didFinishLaunching. Viene visualizzato l'avviso. Soluzione: lascia che lo stack si svolga prima, presenta il modal vc in un altro metodo, invocato con performSelector withDelay: 0.0.
danh

9
Ed ecco un'altra domanda che spiega perché performSelector withDelay funziona. stackoverflow.com/questions/1922517/…
fatih

1
La soluzione di danh ha funzionato per me, ma ho dovuto usare 0.1 invece di 0.0.
Brandon O'Rourke,

11
Invece di utilizzare un performSelectorWithDelay pari a zero, eseguilo in viewDidAppear anziché in viewDidLoad o altro.
tooluser

40

Ho risolto questo errore cambiando animato da SÌ a NO.

A partire dal:

[tabBarController presentModalViewController:viewController animated:YES];

Per:

[tabBarController presentModalViewController:viewController animated:NO];

4
Questo risolve il problema se non si preoccupano di animazione, ma se avete bisogno di animazione: SI, cercano il commento di Danh sulla risposta accettata: stackoverflow.com/questions/7886096/...
wxactly

3
FYI: presentModalViewController: animated: è stato deprecato in iOS6.
ZS

16

Come pubblicato da danh

È possibile generare questo avviso presentando la vc modale prima che l'app venga inizializzata. es. Avvia un'app modello di applicazione a schede e presenta un vc modale sopra self.tabBarController come ultima riga dell'applicazione: didFinishLaunching. Viene visualizzato l'avviso. Soluzione: lascia che lo stack si svolga prima, presenta il modal vc in un altro metodo, invocato con performSelector withDelay: 0.0

Prova a spostare il metodo nel viewWillAppear e proteggilo in modo che venga eseguito solo una volta (consiglierei di impostare una proprietà)


Perché viewWillAppeare no viewDidAppear?
CyberMew

6

Un'altra soluzione per molti casi è assicurarsi che la transizione tra UIViewControllers avvenga al termine della procedura non adatta (come durante l'inizializzazione), eseguendo:

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

Questo è generale anche per pushViewController:animated:, ecc.


4

Ho avuto lo stesso problema. Ho chiamato un metodo viewDidLoadall'interno del mio primoUIViewController

- (void)viewDidLoad{
    [super viewDidLoad];

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

- (void)loadingView{

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

All'interno del secondo UIViewControllerho fatto lo stesso anche con 0,5 secondi di ritardo. Dopo aver modificato il ritardo su un valore più alto, ha funzionato bene. È come se il segue non potesse essere eseguito troppo velocemente dopo un altro segue.


7
Il metodo del ciclo di vita della vista viewDidAppear viene fornito esattamente a questo scopo e sarebbe più affidabile dell'introduzione di un ritardo artificiale, fwiw.
tooluser

1
Questa è la risposta giusta tranne che un ritardo di 0 è sufficiente per attendere che il controller di navigazione sia pronto per una nuova navigazione.
malhal

È totalmente corretto, devi chiamarlo dentro viewDidAppearcosì UINavigationControllerè pronto a gestirlo. Ho cambiato il mio post in questo;)
Alex Cio

Sento che questo dovrebbe essere spostato in viewWillAppear, quindi non devi preoccuparti se la vista è stata inizializzata o meno.
Horsejockey

3

Ho avuto lo stesso problema quando ho bisogno di presentare il mio controller di visualizzazione di accesso da un altro controller di visualizzazione Se l'utente non è autorizzato, l'ho fatto nel metodo ViewDidLoad del mio altro controller di visualizzazione (se non autorizzato -> presentModalViewController). Quando ho iniziato a farlo con il metodo ViewDidAppear, ho risolto questo problema. Penso che ViewDidLoad inizializzi solo le proprietà e successivamente inizi l'algoritmo di visualizzazione effettivo! Ecco perché è necessario utilizzare il metodo viewDidAppear per effettuare transizioni modali!


3

Ho avuto questo problema a causa di un errore di battitura:

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

invece di

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

Si chiamava "WillAppear" nel super invece di "DidAppear"


2

Ho avuto molti problemi con lo stesso problema. Ho risolto questo problema

  1. Avvio di ViewController utilizzando il metodo storyboad instantiateViewControllerWithIdentifier. vale a direIntro *vc = [self.storyboard instantiateViewControllerWithIdentifier:@"introVC"];
  2. [self.tabBarController presentModalViewController : vc animated:YES];

Ho il viewcontroller nel mio storyboard, per qualche motivo il solo utilizzo [[introvc alloc] init];non ha funzionato per me.


1
bello vederti usare la nuova funzione di storyboard. ma non stavo usando lo storyboard nel mio caso ...
Raptor

Volevo solo sottolineare che "instantiateViewControllerWithIdentifier" prende l'identificatore del controller. Per ulteriori informazioni, controllare stackoverflow.com/questions/8186375/...
Kishor Kundan

2

L'ho risolto scrivendo

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

3
Cordiali saluti per essere più idiomatico (e più sicuro!) Dovresti fare: animato: SI completamento: zero
powerj1984

2
Ti concedo più idiomatica, ma come è più sicuro?
Zev Eisenberg

2

Ho avuto questo problema con un codice di terze parti. Qualcuno ha dimenticato di impostare il super all'interno di viewWillAppear e viewWillDisappear in una classe TabBarController personalizzata.

- (void) viewWillAppear:(BOOL)animated {

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

or

- (void) viewWillDisappear:(BOOL)animated {

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

2

Se stai usando transitioningDelegate(non il caso nell'esempio di questa domanda), imposta anche modalPresentationStylesu .Custom.

veloce

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

1

Ho avuto lo stesso errore. Ho una barra delle schede con 3 elementi e stavo inconsciamente cercando di chiamare il controller della vista principale dell'elemento 1 nell'elemento 2 della mia barra delle schede usando performSegueWithIdentifier.

Quello che succede è che chiama il controller di visualizzazione e torna al controller di visualizzazione principale dell'elemento 2 dopo alcuni secondi e registra l'errore.

Apparentemente, non puoi chiamare il controller di visualizzazione principale di un elemento su un altro elemento.

Quindi invece di performSegueWithIdentifier

ero solito [self.parentViewController.tabBarController setSelectedIndex:0];

Spero che questo aiuti qualcuno.


1

Ho avuto lo stesso problema e ho pensato di postare nel caso in cui qualcun altro si imbattesse in qualcosa di simile.

Nel mio caso, avevo collegato un riconoscitore di gesti di pressione prolungata al mio UITableViewController.

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

Nel mio selettore onLongPress, ho lanciato il mio controller di visualizzazione successivo.

- (IBAction)onLongPress:(id)sender {

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

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

    [page release];

}

Nel mio caso, ho ricevuto il messaggio di errore perché il riconoscimento della pressione prolungata è stato attivato più di una volta e, di conseguenza, il mio "SomeViewController" è stato inserito più volte nello stack.

La soluzione era aggiungere un valore booleano per indicare quando SomeViewController era stato inserito nello stack. Quando è stato chiamato il metodo viewWillAppear del mio UITableViewController, ho impostato il valore booleano su NO.


1

Ho scoperto che, se stai usando uno storyboard, vorrai mettere il codice che presenta il nuovo controller di visualizzazione in viewDidAppear. Eliminerà anche l'avviso "La presentazione dei controller di visualizzazione su controller di visualizzazione scollegati è sconsigliata".


1

In Swift 2+ per me funziona:

Ho UITabBarViewController nello storyboard e avevo selezionato la proprietàIndex in questo modo:

inserisci qui la descrizione dell'immagine

Ma lo elimino e aggiungo nel mio metodo viewDidLoad della mia classe iniziale, in questo modo:

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

Spero di poter aiutare qualcuno.


0

In realtà è necessario attendere fino al termine dell'animazione push. Quindi puoi delegare UINavigationController e impedire il push fino al termine dell'animazione.

- (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];
    }
}

Lo chiamo quando una cella selezionata. Dipende da te in realtà
ymutlu

0

Come suggerito da @danh, il mio problema era che stavo presentando il modal vc prima che UITabBarControllerfosse pronto. Tuttavia, mi sono sentito a disagio a fare affidamento su un ritardo fisso prima di presentare il controller della vista (dai miei test, avevo bisogno di utilizzare un ritardo di 0,05-0,1 secondi performSelector:withDelay:). La mia soluzione è quella di aggiungere un blocco che viene chiamato il UITabBarController's viewDidAppear:metodo:

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

Ora 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;
};

0

devi assicurarti che - (void) beginAppearanceTransition: (BOOL) isAppearing animated: (BOOL) animated e - (void) endAppearanceTransition sia creato insieme nella classe.


0

Ho avuto lo stesso problema. Durante lo sviluppo volevo bypassare gli schermi. Stavo navigando da un controller di visualizzazione a un altro in viewDidLoad chiamando un metodo di selezione.

Il problema è che dovremmo lasciare che ViewController finisca la transizione prima di passare a un altro ViewController.

Questo ha risolto il mio problema: il ritardo è necessario per consentire ai ViewController di terminare la transizione prima di passare a un altro.

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

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

    }

-1

Ho avuto questo problema quando sono passato da TVC root a TVC A e poi TVC B. Dopo aver toccato il pulsante "load" in TVC BI volevo tornare direttamente alla TVC root (non c'è bisogno di rivisitare TVC A quindi perché farlo) . Avevo:

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

... che ha dato l'errore "Chiamate sbilanciate all'inizio / fine ecc.". Quanto segue ha risolto l'errore, ma nessuna animazione:

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

Questa era la mia soluzione finale, nessun errore e ancora animata:

//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

Ho riscontrato questo errore quando ho collegato un pulsante UIB a un'azione segue dello storyboard (in IB) ma in seguito ho deciso di chiamare il pulsante in modo programmatico performSegueWithIdentifier dimenticando di rimuovere il primo da IB.

In sostanza, ha eseguito la chiamata segue due volte, ha fornito questo errore e in realtà ha spinto la mia vista due volte. La soluzione era rimuovere una delle chiamate segue.

Spero che questo aiuti qualcuno stanco come me!

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.