Llamadas no balanceadas para comenzar / finalizar transiciones de apariencia para <UITabBarController: 0x197870>

119

Leí SO sobre otro usuario que encontró un error similar , pero este error es en un caso diferente.

Recibí este mensaje cuando agregué un controlador de vista inicialmente:

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

La estructura de la aplicación es la siguiente:

Tengo un TabBarController de 5 pestañas vinculado a 5 View Controllers. En la pestaña de visualización inicial, llamo un nuevo controlador de vista para superponer como una introducción de la aplicación.

Utilizo este código para llamar al controlador de vista de introducción:

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

Después de que IntroVCaparezca este controlador de vista, se mostrará el error anterior.

ps Estoy usando xCode 4.2 y iOS 5.0 SDK, desarrollando la aplicación iOS 4.3.

Raptor
fuente
Hola Shivan, tengo el mismo problema contigo. Pero todavía no puedo solucionarlo después de ver las respuestas a continuación. ¿Puedo saber dónde llamas al controlador de vista de introducción?
ZYiOS

Respuestas:

98

Sin ver más del código circundante, no puedo dar una respuesta definitiva, pero tengo dos teorías.

  1. No está utilizando UIViewControllerel inicializador designadoinitWithNibName:bundle: . Intente usarlo en lugar de solo init.

  2. Además, selfpuede ser uno de los controladores de vista del controlador de la barra de pestañas. Presente siempre los controladores de vista desde el controlador de vista superior, lo que significa que en este caso pida al controlador de la barra de pestañas que presente el controlador de vista de superposición en nombre del controlador de vista. Aún puede mantener los delegados de devolución de llamada en el controlador de vista real, pero debe tener el controlador de la barra de pestañas presente y descartarlo.

Jesper
fuente
2
# 1 solucionó este problema para mí, usé initWithNibName: nil bundle: nil en lugar de init.
Hua-Ying
172
Puede generar esta advertencia presentando el vc modal antes de que la aplicación termine de inicializarse. es decir, inicie una aplicación de plantilla de aplicación con pestañas y presente un vc modal sobre self.tabBarController como última línea en la aplicación: didFinishLaunching. Aparece una advertencia. Solución: deje que la pila se desenrolle primero, presente el vc modal en otro método, invocado con un performSelector withDelay: 0.0.
danh
9
Y aquí hay otra pregunta que explica por qué funciona performSelector withDelay. stackoverflow.com/questions/1922517/…
fatih
1
La solución de danh funcionó para mí, pero tuve que usar 0.1 en lugar de 0.0.
Brandon O'Rourke
11
En lugar de utilizar un performSelectorWithDelay de cero, realice esto en viewDidAppear en lugar de viewDidLoad o cualquier otra cosa.
tooluser
40

Solucioné este error cambiando la animación de SÍ a NO.

De:

[tabBarController presentModalViewController:viewController animated:YES];

A:

[tabBarController presentModalViewController:viewController animated:NO];
PokerIncome.com
fuente
4
Esto soluciona el problema si no le importa la animación, pero si necesita animación: SÍ, pruebe el comentario de danh sobre la respuesta aceptada: stackoverflow.com/questions/7886096/…
wxactly
3
Para su información: presentModalViewController: animated: fue obsoleto en iOS6.
ZS
16

Según lo publicado por danh

Puede generar esta advertencia presentando el vc modal antes de que la aplicación se haya inicializado. es decir, inicie una aplicación de plantilla de aplicación con pestañas y presente un vc modal sobre self.tabBarController como última línea en la aplicación: didFinishLaunching. Aparece una advertencia. Solución: deje que la pila se desenrolle primero, presente el vc modal en otro método, invocado con un performSelector withDelay: 0.0

Intente mover el método a viewWillAppear y guárdelo para que se ejecute solo una vez (recomendaría configurar una propiedad)

Peter Lapisu
fuente
¿Por qué viewWillAppeary no viewDidAppear?
CyberMew
6

Otra solución para muchos casos es asegurarse de que la transición entre UIViewControllers ocurra después de que finalice el procedimiento no adecuado (como durante la inicialización), haciendo lo siguiente:

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

Esto es general para también pushViewController:animated:, etc.

mllm
fuente
4

Yo tuve el mismo problema. Llamé a un método viewDidLoaddentro de mi primeraUIViewController

- (void)viewDidLoad{
    [super viewDidLoad];

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

- (void)loadingView{

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

Dentro del segundo UIViewControllerhice lo mismo también con 0,5 segundos de retraso. Después de cambiar el retraso a un valor más alto, funcionó bien. Es como si la transición no se pudiera realizar demasiado rápido después de otra transición.

Alex Cio
fuente
7
El método viewDidAppear del ciclo de vida de la vista se proporciona exactamente para este propósito y sería más confiable que introducir un retraso artificial, fwiw.
tooluser
1
Esta es la respuesta correcta, excepto que un retraso de 0 es suficiente para esperar hasta que el controlador de navegación esté listo para una nueva navegación.
Malhal
Es totalmente correcto, tienes que llamarlo adentro viewDidAppearpara que UINavigationControlleresté listo para manejarlo. Cambié mi publicación a esta;)
Alex Cio
Siento que esto debería moverse a viewWillAppear, entonces no tiene que preocuparse por si la vista se ha inicializado o no.
horsejockey
3

Tuve el mismo problema cuando necesito presentar mi controlador de vista de inicio de sesión desde otro controlador de vista. Si el usuario no está autorizado, lo hice en el método ViewDidLoad de mi otro controlador de vista (si no está autorizado -> presentModalViewController). Cuando empiezo a hacerlo en el método ViewDidAppear, resolví este problema. ¡Creo que ViewDidLoad solo inicializa propiedades y luego comienza el algoritmo de visualización de visualización real! ¡Es por eso que debe usar el método viewDidAppear para hacer transiciones modales!

Tolusha
fuente
3

Tuve este problema debido a un error tipográfico:

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

en vez de

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

Estaba llamando "WillAppear" en el super en lugar de "DidAppear"

Adriano Spadoni
fuente
2

Tuve muchos problemas con el mismo problema. Resolví este por

  1. Iniciando ViewController utilizando el método storyboad instantiateViewControllerWithIdentifier. es decirIntro *vc = [self.storyboard instantiateViewControllerWithIdentifier:@"introVC"];
  2. [self.tabBarController presentModalViewController : vc animated:YES];

Tengo el controlador de vista en mi guión gráfico, por alguna razón, usar solo [[introvc alloc] init];no funcionó para mí.

Mogambolal
fuente
1
Me alegro de verte usando la nueva función de guión gráfico. pero no estaba usando el guión gráfico en mi caso ...
Raptor
Solo quería señalar esto que "instantiateViewControllerWithIdentifier" toma el identificador del controlador. para obtener más detalles, visite stackoverflow.com/questions/8186375/…
Kishor Kundan
2

Lo resolví escribiendo

[self.navigationController presentViewController:viewController 
                                        animated:TRUE 
                                      completion:NULL];
pankesh
fuente
3
Para su información, para ser más idiomático (¡y más seguro!), Debe hacer: animado: SÍ finalización: nulo
powerj1984
2
Te concederé más idiomático, pero ¿cómo es más seguro?
Zev Eisenberg
2

Tuve este problema con un código de terceros. Alguien olvidó configurar el superdentro de viewWillAppear y viewWillDisappear en una clase TabBarController personalizada.

- (void) viewWillAppear:(BOOL)animated {

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

or

- (void) viewWillDisappear:(BOOL)animated {

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

Si está usando transitioningDelegate(no es el caso en el ejemplo de esta pregunta), configure también modalPresentationStyleen .Custom.

Rápido

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

Yo tenía el mismo error. Tengo una barra de pestañas con 3 elementos e inconscientemente estaba tratando de llamar al controlador de vista raíz del elemento 1 en el elemento 2 de mi barra de pestañas usando performSegueWithIdentifier.

Lo que sucede es que llama al controlador de vista y vuelve al controlador de vista raíz del elemento 2 después de unos segundos y registra ese error.

Aparentemente, no puede llamar al controlador de vista raíz de un elemento a otro elemento.

Entonces en lugar de performSegueWithIdentifier

solía [self.parentViewController.tabBarController setSelectedIndex:0];

Espero que esto ayude a alguien.

Gellie Ann
fuente
1

Tuve el mismo problema y pensé en publicar en caso de que alguien más se encuentre con algo similar.

En mi caso, había adjuntado un reconocedor de gestos de pulsación prolongada a mi UITableViewController.

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

En mi selector onLongPress, lancé mi siguiente controlador de vista.

- (IBAction)onLongPress:(id)sender {

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

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

    [page release];

}

En mi caso, recibí el mensaje de error porque el reconocedor de pulsación larga se disparó más de una vez y, como resultado, mi "SomeViewController" se insertó en la pila varias veces.

La solución fue agregar un booleano para indicar cuándo se había insertado SomeViewController en la pila. Cuando se llamó al método viewWillAppear de mi UITableViewController, volví a establecer el booleano en NO.

Dale Moore
fuente
1

Descubrí que, si está utilizando un guión gráfico, querrá poner el código que presenta el nuevo controlador de vista en viewDidAppear. También eliminará la advertencia "No se recomienda presentar controladores de vista en controladores de vista separados".

Dan Levy
fuente
1

En Swift 2+ para mí funciona:

Tengo UITabBarViewController en el guión gráfico y tenía la propiedad selectedIndex como esta:

ingrese la descripción de la imagen aquí

Pero lo elimino y agrego mi método viewDidLoad de mi clase inicial, así:

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

Espero poder ayudar a alguien.

Dasoga
fuente
0

En realidad, debe esperar hasta que finalice la animación de inserción. De modo que puede delegar UINavigationController y evitar presionar hasta que finalice la animación.

- (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
fuente
Lo llamo cuando se selecciona una celda. Depende de ti en realidad
ymutlu
0

Como sugirió @danh, mi problema fue que estaba presentando el modal vc antes de que UITabBarControllerestuviera listo. Sin embargo, me sentí incómodo al depender de un retraso fijo antes de presentar el controlador de vista (según mis pruebas, necesitaba usar un retraso de 0.05-0.1 s performSelector:withDelay:). Mi solución es añadir un bloque que es llamada en UITabBarController's viewDidAppear:método:

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

Ahora en 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
fuente
0

necesita asegurarse de que - (void) beginAppearanceTransition: (BOOL) isAppearing animado: (BOOL) animado y - (void) endAppearanceTransition se crean juntos en la clase.

zszen
fuente
0

Tuve el mismo problema. Al desarrollar quería evitar las pantallas. Estaba navegando de un controlador de vista a otro en viewDidLoad llamando a un método selector.

El problema es que debemos dejar que ViewController finalice la transición antes de pasar a otro ViewController.

Esto resolvió mi problema: el retraso es necesario para permitir que ViewControllers finalice la transición antes de pasar a otro.

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

Rápido 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
fuente
-1

Tuve este problema cuando navegué desde la raíz TVC a TVC A y luego a TVC B. Después de tocar el botón "cargar" en TVC BI, quería volver directamente a la raíz TVC (no es necesario volver a visitar TVC A, entonces, ¿por qué hacerlo) . Yo tenía:

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

... que dio el error "Llamadas no balanceadas para comenzar / finalizar, etc.". Lo siguiente solucionó el error, pero no la animación:

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

Esta fue mi solución final, sin error y aún animada:

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

Encontré este error cuando conecté un UIButton a una acción de segue del guión gráfico (en IB) pero luego decidí hacer que el botón llamara programáticamente performSegueWithIdentifier olvidándose de eliminar el primero de IB.

En esencia, realizó la llamada de segue dos veces, dio este error y, de hecho, presionó mi vista dos veces. La solución fue eliminar una de las llamadas segue.

¡Espero que esto ayude a alguien tan cansado como yo!

capikaw
fuente