Error de aplicación de iOS: no se puede agregar uno mismo como subvista


157

Recibí este informe de bloqueo, pero no sé cómo depurarlo.

Fatal Exception NSInvalidArgumentException
Can't add self as subview
0 ...    CoreFoundation  __exceptionPreprocess + 130
1    libobjc.A.dylib     objc_exception_throw + 38
2    CoreFoundation  -[NSException initWithCoder:]
3    UIKit   -[UIView(Internal) _addSubview:positioned:relativeTo:] + 110
4    UIKit   -[UIView(Hierarchy) addSubview:] + 30
5    UIKit   __53-[_UINavigationParallaxTransition animateTransition:]_block_invoke + 1196
6    UIKit   +[UIView(Animation) performWithoutAnimation:] + 72
7    UIKit   -[_UINavigationParallaxTransition animateTransition:] + 732
8    UIKit   -[UINavigationController _startCustomTransition:] + 2616
9    UIKit   -[UINavigationController _startDeferredTransitionIfNeeded:] + 418
10   UIKit   -[UINavigationController __viewWillLayoutSubviews] + 44
11   UIKit   -[UILayoutContainerView layoutSubviews] + 184
12   UIKit   -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 346
13   QuartzCore  -[CALayer layoutSublayers] + 142
14   QuartzCore  CA::Layer::layout_if_needed(CA::Transaction*) + 350
15   QuartzCore  CA::Layer::layout_and_display_if_needed(CA::Transaction*) + 16
16   QuartzCore  CA::Context::commit_transaction(CA::Transaction*) + 228
17   QuartzCore  CA::Transaction::commit() + 314
18   QuartzCore  CA::Transaction::observer_callback(__CFRunLoopObserver*, unsigned long, void*) + 56

La versión de iOS es 7.0.3. ¿Alguien experimenta este extraño accidente?

ACTUALIZAR:

No sé en qué parte de mi código causó este bloqueo, así que no puedo publicar el código aquí, lo siento.

Segunda ACTUALIZACIÓN

Vea la respuesta a continuación.


3
¿Nos puede mostrar su código?
David Gölzhäuser

43
Lo siento, pero no entiendo tu reacción exagerada. El error de la pila es claro sobre el problema. Entonces, primero, puede permitir que el usuario ponga más código como se le solicitó (solo 1 hora la pregunta que se le hizo y usted solicita cerrarla de inmediato). En segundo lugar, recibí un voto negativo sin razón, ya que mi respuesta es clara. La pregunta es "¿Alguien ha experimentado este extraño accidente?". Y le dije por qué consiguió esto. Incluso si no se encuentra específicamente en su código.
Tancrede Chazallet

9
Esta pregunta es correcta. El usuario no puede dar el código exacto de error en esta situación. porque no sabe en qué controlador de vista algo va mal
Ravindra Bagale

16
Usamos Crashlytics y tenemos más de 30 usuarios que han bloqueado nuestra aplicación con el mensaje "No se puede agregar como subvista", por supuesto, no tenemos código que intente agregarse como una subvista. Desde el rastreo no hay ninguna referencia a nuestra aplicación.
Richie Hyatt

49
Votar para reabrir; las personas que lo cierran no hacen mucho desarrollo de iOS, aparentemente, ya que este es un problema común introducido por iOS7 y mata a un montón de aplicaciones que estaban bien en iOS6 (lo he visto en múltiples proyectos de diferentes compañías). Es una pena que esta pregunta sea un éxito en Google, pero algunas personas miopes la cerraron.
Adam

Respuestas:


51

Estoy especulando sobre la base de algo similar que depuré recientemente ... si presionas (o haces estallar) un controlador de vista con Animated: SÍ no se completa de inmediato, y suceden cosas malas si haces otro push o pop antes de la animación completa. Puede probar fácilmente si este es realmente el caso cambiando temporalmente sus operaciones Push y Pop a Animated: NO (para que se completen sincrónicamente) y ver si eso elimina el bloqueo. Si este es realmente su problema y desea volver a activar la animación, entonces la estrategia correcta es implementar el protocolo UINavigationControllerDelegate. Esto incluye el siguiente método, que se llama después de completar la animación:

navigationController:didShowViewController:animated:

Básicamente, desea mover algo de código según sea necesario a este método para asegurarse de que no se produzcan otras acciones que puedan causar un cambio en la pila NavigationController hasta que finalice la animación y la pila esté lista para más cambios.


Hace mucho tiempo, en iOS 4, algo, vi algo similar en una de nuestras aplicaciones: IIRC, si aparecía animado y luego inmediatamente lo animaba, el código de la interfaz de usuario se arruinaría. Terminé simplemente cambiando para nunca hacer dos operaciones animadas push / pop consecutivas. Por supuesto, toda la lógica subyacente se ha reescrito desde entonces, pero no es difícil creer que un error similar aún no esté allí.
Hot Licks

Tuve el mismo problema. En mi caso, esto sucedió porque la aplicación ejecutó una instrucción que cambia la interfaz de [newViewController setLabelTitle:...]usuario del nuevo controlador de vista justo después de llamar a pushViewController con Animated:YES.Y resolví mover el método setLabelTitle para verDidLoad en el nuevo ViewController. Gracias por darme la pista.
jeprubio

Me alegra que haya ayudado! Un buen punto es que mover el código al nuevo ViewController también es una opción si sabes qué clase será. Cada vez me resulta más útil detectar los diversos métodos del protocolo UINavigationControllerDelegate, en cualquier caso. Y descubrí que en iOS8 los eventos se disparan en diferentes órdenes, y algunas cosas que solían ser más o menos sincrónicas ahora regresan rápidamente, pero programan cosas para que se realicen en segundo plano de forma asincrónica, creando muchos errores de sincronización nuevos como estos. Gracias Apple!
RobP

14

También comenzamos a tener este problema, y ​​era muy probable que los nuestros fueran causados ​​por el mismo problema.

En nuestro caso, tuvimos que extraer datos del back-end en algunos casos, lo que significaba que un usuario podía tocar algo y luego habría un ligero retraso antes de que ocurriera el empuje de navegación. Si un usuario estaba haciendo tapping rápidamente, podría terminar con dos pulsaciones de navegación desde el mismo controlador de vista, lo que provocó esta misma excepción.

Nuestra solución es una categoría en UINavigationController que evita los empujes / estallidos a menos que el vc superior sea el mismo desde un punto dado en el tiempo.

archivo .h:

@interface UINavigationController (SafePushing)

- (id)navigationLock; ///< Obtain "lock" for pushing onto the navigation controller

- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated navigationLock:(id)navigationLock; ///< Uses a horizontal slide transition. Has no effect if the view controller is already in the stack. Has no effect if navigationLock is not the current lock.
- (NSArray *)popToViewController:(UIViewController *)viewController animated:(BOOL)animated navigationLock:(id)navigationLock; ///< Pops view controllers until the one specified is on top. Returns the popped controllers. Has no effect if navigationLock is not the current lock.
- (NSArray *)popToRootViewControllerAnimated:(BOOL)animated navigationLock:(id)navigationLock; ///< Pops until there's only a single view controller left on the stack. Returns the popped controllers. Has no effect if navigationLock is not the current lock.

@end

archivo .m:

@implementation UINavigationController (SafePushing)

- (id)navigationLock
{
    return self.topViewController;
}

- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated navigationLock:(id)navigationLock
{
    if (!navigationLock || self.topViewController == navigationLock) 
        [self pushViewController:viewController animated:animated];
}

- (NSArray *)popToRootViewControllerAnimated:(BOOL)animated navigationLock:(id)navigationLock
{
    if (!navigationLock || self.topViewController == navigationLock)
        return [self popToRootViewControllerAnimated:animated];
    return @[];
}

- (NSArray *)popToViewController:(UIViewController *)viewController animated:(BOOL)animated navigationLock:(id)navigationLock
{
    if (!navigationLock || self.topViewController == navigationLock)
        return [self popToViewController:viewController animated:animated];
    return @[];
}

@end

Hasta ahora, esto parece haber resuelto el problema para nosotros. Ejemplo:

id lock = _dataViewController.navigationController.navigationLock;
[[MyApi sharedClient] getUserProfile:_user.id success:^(MyUser *user) {
    ProfileViewController *pvc = [[ProfileViewController alloc] initWithUser:user];
    [_dataViewController.navigationController pushViewController:pvc animated:YES navigationLock:lock];
}];

Básicamente, la regla es: antes de cualquier retraso no relacionado con el usuario tome un bloqueo del controlador de navegación relevante e inclúyalo en la llamada para pulsar / abrir.

La palabra "bloqueo" puede ser una redacción ligeramente pobre, ya que puede insinuar que hay alguna forma de bloqueo que necesita desbloqueo, pero como no hay un método de "desbloqueo" en ningún lado, probablemente esté bien.

(Como nota al margen, "demoras no relacionadas con el usuario" son cualquier demora que el código está causando, es decir, cualquier cosa asincrónica. Los usuarios que tocan un controlador de navegación que se empuja animadamente no cuentan y no hay necesidad de hacer la versión navigationLock: para esos casos.)


Desde que dijo que estaba probando esta solución, ¿ha resuelto el problema por usted?
Mike D

Hasta el momento si. El problema no ha resurgido. Actualizaré la respuesta.
Kalle

44
Utilicé una versión modificada basada en la suya: gist.github.com/mdewolfe/9369751 . Parece que lo ha solucionado.
Mike D

2
@Kalle Esta solución funciona para push / pop. Pero, ¿cómo resolver este error si uso segue?
Geek

@Kadle ¿Me pueden ayudar a implementar esto? Mire stackoverflow.com/q/23247713/1323014 THX
Marckaraujo el

12

Este código resuelve el problema: https://gist.github.com/nonamelive/9334458

Utiliza una API privada, pero puedo confirmar que es seguro en App Store. (Una de mis aplicaciones que usa este código fue aprobada por la App Store).

@interface UINavigationController (DMNavigationController)

- (void)didShowViewController:(UIViewController *)viewController animated:(BOOL)animated;

@end

@interface DMNavigationController ()

@property (nonatomic, assign) BOOL shouldIgnorePushingViewControllers;

@end

@implementation DMNavigationViewController

#pragma mark - Push

- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    if (!self.shouldIgnorePushingViewControllers)
    {
        [super pushViewController:viewController animated:animated];
    }

    self.shouldIgnorePushingViewControllers = YES;
}

#pragma mark - Private API

// This is confirmed to be App Store safe.
// If you feel uncomfortable to use Private API, you could also use the delegate method navigationController:didShowViewController:animated:.
- (void)didShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    [super didShowViewController:viewController animated:animated];
    self.shouldIgnorePushingViewControllers = NO;
}

Esta ha sido la mejor solución hasta ahora, con algunos de los otros todavía obtendría aleatoriamente el problema de doble empuje o obtendría un controlador de navegación congelado.
blueice

Este código no se compila para mí, ¿falta algo?
Maxime B

8

Describiré más detalles sobre este bloqueo en mi aplicación y lo marcaré como respondido.

Mi aplicación tiene un UINavigationController con el controlador raíz es un UITableViewController que contiene una lista de objetos de nota. El objeto de nota tiene una propiedad de contenido en html. Seleccione una nota que irá al controlador de detalles.

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    //get note object
    DetailViewController *controller = [[DetailViewController alloc] initWithNote:note];
    [self.navigationController pushViewController:controller animated:YES];
}

Controlador de detalle

Este controlador tiene un UIWebView, muestra el contenido de la nota pasada desde el controlador raíz.

- (void)viewDidLoad
{
    ...
    [_webView loadHTMLString:note.content baseURL:nil];
    ...
}

Este controlador es el delegado del control webview. Si la nota contiene enlaces, toque un enlace que irá al navegador web integrado en la aplicación.

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
    WebBrowserViewController *browserController = [[WebBrowserViewController alloc] init];
    browserController.startupURL = request.URL;
    [self.navigationController pushViewController:webViewController animated:YES];
    return NO;
}

Recibí el informe de bloqueo anterior todos los días. No sé en qué parte de mi código causó este bloqueo. Después de algunas investigaciones con la ayuda de un usuario, finalmente pude solucionar este bloqueo. Este contenido html provocará el bloqueo:

...
<iframe src="http://google.com"></iframe>
...

En el método viewDidLoad del controlador de detalles, cargué este html en el control webview, justo después de eso, el método delegado anterior se llamó inmediatamente con request.URL es la fuente del iframe (google.com). Este método delegado llama al método pushViewController mientras está en viewDidLoad => crash!

Solucioné este bloqueo marcando el tipo de navegación:

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
    if (navigationType != UIWebViewNavigationTypeOther)
    {
        //go to web browser controller
    }
}

Espero que esto ayude


1
¿No sería una buena opción empujar el controlador sin animación cuando se llama desde viewDidLoad?
Rivera

6

Tuve el mismo problema, lo que simplemente funcionó para mí fue cambiar Animated: Yes a Animated: No.

Parece que el problema se debió a que la animación no se completó a tiempo.

Espero que esto ayude a alguien.


3

Para reproducir este error, intente presionar dos controladores de vista al mismo tiempo. O empujando y haciendo estallar al mismo tiempo. Ejemplo:

ingrese la descripción de la imagen aquí He creado una categoría que intercepta estas llamadas y las hace seguras asegurándose de que no haya otros empujes mientras uno está en progreso. Simplemente copie el código en su proyecto y, debido al método swizzling, estará listo.

#import "UINavigationController+Consistent.h"
#import <objc/runtime.h>
/// This char is used to add storage for the isPushingViewController property.
static char const * const ObjectTagKey = "ObjectTag";

@interface UINavigationController ()
@property (readwrite,getter = isViewTransitionInProgress) BOOL viewTransitionInProgress;

@end

@implementation UINavigationController (Consistent)

- (void)setViewTransitionInProgress:(BOOL)property {
    NSNumber *number = [NSNumber numberWithBool:property];
    objc_setAssociatedObject(self, ObjectTagKey, number , OBJC_ASSOCIATION_RETAIN);
}


- (BOOL)isViewTransitionInProgress {
    NSNumber *number = objc_getAssociatedObject(self, ObjectTagKey);

    return [number boolValue];
}


#pragma mark - Intercept Pop, Push, PopToRootVC
/// @name Intercept Pop, Push, PopToRootVC

- (NSArray *)safePopToRootViewControllerAnimated:(BOOL)animated {
    if (self.viewTransitionInProgress) return nil;
    if (animated) {
        self.viewTransitionInProgress = YES;
    }
    //-- This is not a recursion, due to method swizzling the call below calls the original  method.
    return [self safePopToRootViewControllerAnimated:animated];

}


- (NSArray *)safePopToViewController:(UIViewController *)viewController animated:(BOOL)animated {
    if (self.viewTransitionInProgress) return nil;
    if (animated) {
        self.viewTransitionInProgress = YES;
    }
    //-- This is not a recursion, due to method swizzling the call below calls the original  method.
    return [self safePopToViewController:viewController animated:animated];
}


- (UIViewController *)safePopViewControllerAnimated:(BOOL)animated {
    if (self.viewTransitionInProgress) return nil;
    if (animated) {
        self.viewTransitionInProgress = YES;
    }
    //-- This is not a recursion, due to method swizzling the call below calls the original  method.
    return [self safePopViewControllerAnimated:animated];
}



- (void)safePushViewController:(UIViewController *)viewController animated:(BOOL)animated {
    self.delegate = self;
    //-- If we are already pushing a view controller, we dont push another one.
    if (self.isViewTransitionInProgress == NO) {
        //-- This is not a recursion, due to method swizzling the call below calls the original  method.
        [self safePushViewController:viewController animated:animated];
        if (animated) {
            self.viewTransitionInProgress = YES;
        }
    }
}


// This is confirmed to be App Store safe.
// If you feel uncomfortable to use Private API, you could also use the delegate method navigationController:didShowViewController:animated:.
- (void)safeDidShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
    //-- This is not a recursion. Due to method swizzling this is calling the original method.
    [self safeDidShowViewController:viewController animated:animated];
    self.viewTransitionInProgress = NO;
}


// If the user doesnt complete the swipe-to-go-back gesture, we need to intercept it and set the flag to NO again.
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
    id<UIViewControllerTransitionCoordinator> tc = navigationController.topViewController.transitionCoordinator;
    [tc notifyWhenInteractionEndsUsingBlock:^(id<UIViewControllerTransitionCoordinatorContext> context) {
        self.viewTransitionInProgress = NO;
        //--Reenable swipe back gesture.
        self.interactivePopGestureRecognizer.delegate = (id<UIGestureRecognizerDelegate>)viewController;
        [self.interactivePopGestureRecognizer setEnabled:YES];
    }];
    //-- Method swizzling wont work in the case of a delegate so:
    //-- forward this method to the original delegate if there is one different than ourselves.
    if (navigationController.delegate != self) {
        [navigationController.delegate navigationController:navigationController
                                     willShowViewController:viewController
                                                   animated:animated];
    }
}


+ (void)load {
    //-- Exchange the original implementation with our custom one.
    method_exchangeImplementations(class_getInstanceMethod(self, @selector(pushViewController:animated:)), class_getInstanceMethod(self, @selector(safePushViewController:animated:)));
    method_exchangeImplementations(class_getInstanceMethod(self, @selector(didShowViewController:animated:)), class_getInstanceMethod(self, @selector(safeDidShowViewController:animated:)));
    method_exchangeImplementations(class_getInstanceMethod(self, @selector(popViewControllerAnimated:)), class_getInstanceMethod(self, @selector(safePopViewControllerAnimated:)));
    method_exchangeImplementations(class_getInstanceMethod(self, @selector(popToRootViewControllerAnimated:)), class_getInstanceMethod(self, @selector(safePopToRootViewControllerAnimated:)));
    method_exchangeImplementations(class_getInstanceMethod(self, @selector(popToViewController:animated:)), class_getInstanceMethod(self, @selector(safePopToViewController:animated:)));
}

@end

Un problema con esta solución es que si llama popToRootViewControllero popToViewController:cuando ya está en el controlador de vista raíz o en el control viewController, didShowViewControllerno se llamará y quedará bloqueado viewTransitionInProgress.
divergio

1
¿Puede explicar estas líneas: self.interactivePopGestureRecognizer.delegate = (id<UIGestureRecognizerDelegate>)viewController; [self.interactivePopGestureRecognizer setEnabled:YES]; cuándo se deshabilitó el reconocedor? ¿Y cómo sabes cuál debería ser el delegado? Con esas líneas, para mí rompe el gesto de pop después de aparecer una vez.
divergio

Intenté implementar esto y después de un tiempo bloquea el controlador de navegación, probablemente debido a lo que @divergio mencionó.
blueice

2

Acabo de experimentar este problema también. Déjame mostrarte mi código:

override func viewDidLoad() { 
  super.viewDidLoad()

  //First, I create a UIView
  let firstFrame = CGRect(x: 50, y: 70, height: 200, width: 200)
  let firstView = UIView(frame: firstFrame)
  firstView.addBackgroundColor = UIColor.yellow
  view.addSubview(firstView) 

  //Now, I want to add a subview inside firstView
  let secondFrame = CGRect(x: 20, y:50, height: 15, width: 35)
  let secondView = UIView(frame: secondFrame)
  secondView.addBackgroundColor = UIColor.green
  firstView.addSubView(firstView)
 }

El error aparece debido a esta línea:

firstView.addSubView(firstView)

No puede agregarse a la subvista. Cambié la línea de código a:

firstView.addSubView(secondView)

El error desapareció y pude ver ambas vistas. Solo pensé que esto ayudaría a cualquiera que quisiera ver un ejemplo.


También probé este enfoque, pero el stacktrace sería diferente y en realidad mostraría la línea de su código que causa el bloqueo. Creo que el problema de origen es diferente de la pregunta.
Ben

1

Busque su código para "addSubview".

En uno de los lugares a los que llamó este método, trató de agregar una vista a su propia matriz de subvista utilizando este método.

Por ejemplo:

[self.view addSubview:self.view];

O:

[self.myLabel addSubview:self.myLabel];

Me alegra saber que encontró su error, y ahora entiendo exactamente por qué recibió el mensaje "No se puede agregar a mí mismo como subvista". En un punto donde su View2 era el controlador de vista raíz de su controlador de navegación, presionó View2 que causó esto: por lo [View2.view addSubview:View2.view]tanto, se agregó self como subview.
Michal Shatz

1

Creo que presionar / hacer estallar los controladores de vista con animación en cualquier punto debería estar perfectamente bien y el SDK debería manejar gentilmente la cola de llamadas para nosotros.

Por lo tanto, no lo hace y todas las soluciones intentan ignorar los impulsos posteriores, lo que podría considerarse un error ya que la pila de navegación final no es lo que pretendía el código.

Implementé una cola de llamadas push en su lugar:

// SafeNavigationController.h

@interface SafeNavigationController : UINavigationController
@end

 

// SafeNavigationController.m

#define timeToWaitBetweenAnimations 0.5

@interface SafeNavigationController ()

@property (nonatomic, strong) NSMutableArray * controllersQueue;
@property (nonatomic)         BOOL animateLastQueuedController;
@property (nonatomic)         BOOL pushScheduled;
@property (nonatomic, strong) NSDate * lastAnimatedPushDate;

@end

@implementation SafeNavigationController

- (void)awakeFromNib
{
    [super awakeFromNib];

    self.controllersQueue = [NSMutableArray array];
}

- (void)pushViewController:(UIViewController *)viewController
                  animated:(BOOL)animated
{
    [self.controllersQueue addObject:viewController];
    self.animateLastQueuedController = animated;

    if (self.pushScheduled)
        return;

    // Wait for push animation to finish
    NSTimeInterval timeToWait = self.lastAnimatedPushDate ? timeToWaitBetweenAnimations + [self.lastAnimatedPushDate timeIntervalSinceNow] : 0.0;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)((timeToWait > 0.0 ? timeToWait : 0.0) * NSEC_PER_SEC)),
                   dispatch_get_main_queue(), ^
                   {
                       [self pushQueuedControllers];

                       self.lastAnimatedPushDate = self.animateLastQueuedController ? [NSDate date] : nil;
                       self.pushScheduled = NO;
                   });
    self.pushScheduled = YES;
}

- (void)pushQueuedControllers
{
    for (NSInteger index = 0; index < (NSInteger)self.controllersQueue.count - 1; index++)
    {
        [super pushViewController:self.controllersQueue[index]
                         animated:NO];
    }
    [super pushViewController:self.controllersQueue.lastObject
                     animated:self.animateLastQueuedController];

    [self.controllersQueue removeAllObjects];
}

@end

No maneja colas mixtas de push y pops, pero es un buen comienzo para solucionar la mayoría de nuestros bloqueos.

Gist: https://gist.github.com/rivera-ernesto/0bc628be1e24ff5704ae


He estado probando su solución, que parece muy buena, pero tengo un problema. Cuando presiono 2 controladores de vista con NO animado uno tras otro, veo muy brevemente el primero. Esto no sucedió antes. ¿Alguna idea de lo que puedo hacer para solucionarlo?
Jan

Estoy tratando de construir un proyecto que pueda causar constantemente este tipo de bloqueo (mi proyecto real recibe informes de bloqueo como este). Hice una aplicación simple con un controlador de navegación, un controlador raíz y un botón que inmediatamente empuja 4 nuevos controladores de vista a la pila de navegación, y luego saca el último. Sin ninguna subclase especial ni nada, en realidad parece funcionar bien. ¿Apple solucionó esto recientemente?
Cruinh

1

Perdón por llegar tarde a la fiesta. Recientemente tuve este problema en el que mi barra de navegación entra en estado dañado debido a que presiono más de un controlador de vista al mismo tiempo. Esto sucede porque el otro controlador de vista se presiona mientras el primer controlador de vista todavía está animando. Tomando una pista de la respuesta no acertada, se me ocurrió una solución simple que funciona en mi caso. Solo necesita subclasificar UINavigationControllery anular el método pushViewController y verificar si la animación del controlador de vista anterior ya ha finalizado. Puede escuchar la finalización de la animación haciendo que su clase sea un delegado UINavigationControllerDelegatey establezca el delegado en self.

He subido una esencia aquí para simplificar las cosas.

Solo asegúrese de establecer esta nueva clase como NavigationController en su guión gráfico.


Hasta ahora parece haber solucionado los bloqueos en la aplicación en la que estaba trabajando ... además, la solución es bastante sencilla y clara sobre el punto: la primera animación del controlador de vista aún no estaba completa. Las personas que tienen el mismo problema deben verificar esto.
alasker

0

Basado en la gran sugerencia de @RobP, hice la subclase UINavigationController para evitar tales problemas. Maneja empujar y / o hacer estallar y puede ejecutar con seguridad:

[self.navigationController pushViewController:vc1 animated:YES];
[self.navigationController pushViewController:vc2 animated:YES];
[self.navigationController pushViewController:vc3 animated:YES];
[self.navigationController popViewControllerAnimated:YES];

Si la marca 'acceptConflictingCommands' es verdadera (de forma predeterminada), el usuario verá el empuje animado de vc1, vc2, vc3 y luego verá el estallido animado de vc3. Si 'acceptConflictingCommands' es falso, todas las solicitudes push / pop se descartarán hasta que vc1 se presione completamente, por lo que se descartarán otras 3 llamadas.


¿Son esos comandos realmente conflictivos? Acabo de armar un nuevo proyecto rápido para ver cómo ocurre este bloqueo, usando un código como el que tienes arriba (pero en Swift), y realmente ejecutó cada impulso, y el pop, todo en secuencia. uno después del otro. Sin accidente. Sin usar ninguna subclase. solo el controlador UINavigationController regular de apple.
Cruinh

De hecho, se estaba bloqueando con ObjC e iOS 7. No puedo confirmar si todavía ocurre ahora. ¿Estás seguro de que estás ejecutando los comandos con animated:trueflag?
hris.to

Sí, estaba usando la bandera animada: verdadera.
Cruinh

0

La solución de nonamelive es asombrosa. Pero si no desea utilizar la API privada, simplemente puede lograr el UINavigationControllerDelegatemétodo o puede cambiar la animación YESa NO. Aquí hay una muestra de código, puede heredarlo. Espero que sea útil :)

https://github.com/antrix1989/ANNavigationController



0

Algunas veces, por error, trató de agregar una vista a su propia vista.

halfView.addSubview(halfView)

cambie esto a su vista secundaria.

halfView.addSubview(favView)

0

También me encontré con este problema. Cuando hice el análisis de registro de Firebase, descubrí que este problema solo ocurre cuando la aplicación se inicia en frío. Así que escribí una demostración que puede reproducir este bloqueo.

.

También descubrí que cuando se muestra el controlador de vista raíz de la ventana, realizar varios empujes no causará el mismo problema nuevamente. (Puede comentar testColdStartUp (rootNav) en AppDelegate.swift y descomentar el comentario testColdStartUp () en ViewController.swift)

PD: analicé la escena de este bloqueo en mi aplicación. Cuando el usuario hace clic en la notificación de inserción para iniciar en frío la aplicación, la aplicación todavía está en la página de inicio y hace clic en otra inserción para saltar. En este momento, la aplicación puede aparecer el bloqueo. Mi solución actual es almacenar en caché el inicio en frío del enlace universal o de inserción para abrir la página de salto de la aplicación, esperar a que se muestre el controlador rootview y luego retrasar la ejecución.


-2

pruebe su navegación usando el método de retraso, para completar la última animación de navegación,

[self performSelector:<#(SEL)#> withObject:<#(id)#> afterDelay:<#(NSTimeInterval)#>]


-2

Una vista no se puede agregar como una subvista en sí misma.

Las vistas mantienen una jerarquía de elementos primarios y secundarios, por lo que si agrega una vista como una subvista en sí misma, lo hará por excepción.

si una clase es UIViewController, para obtener su vista, use self.view.

si una clase es UIView Class, entonces para obtener su vista, usa self.


-3

no puede agregar self como subview si va a ser una clase UiViewController. puede agregar self como subview si va a ser una clase UiView.


-9

Si desea agregar una Subvista a una Vista, puede hacerlo así;

UIView *mainview = [[UIView alloc] initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height)]; //Creats the mainview
    UIView *subview = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)]; //Creates the subview, you can use any kind of Views (UIImageView, UIWebView, UIView…)

    [mainview addSubview:subview]; //Adds subview to mainview

Bien hecho, es un buen código. Ahora, ¿puede decirme qué tiene que ver esto con esta pregunta y cómo lo resuelve?
Popeye

@Popeye ¿Tienes una idea mejor?
David Gölzhäuser

No, porque no han proporcionado suficiente información / código para replicar el problema. Así que no hay forma de que esto pueda ser respondido, solo parece que les estás diciendo cómo hacer algo que no tiene nada que ver con su problema.
Popeye

2
Supongo que fue mucho más útil para ellos cerrar el problema. ¡Entendido! +1 para David G por tratar de ayudar a alguien en StackOverflow. ¡Ojalá pudiera -1 tus votos cercanos! Esto todavía está sucediendo para los usuarios y puede ser un error en iOS7 por lo que sabemos. Entonces, el hecho de que alguien no pueda publicar el código ofensivo no significa que la pregunta no sea válida y valiosa para que otros usuarios la vean. Incluso si es solo para ver que otras personas están viendo el mismo problema sin ninguna razón lógica por la que lo están viendo. -rrh
Richie Hyatt
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.