Errore app iOS: impossibile aggiungere sé come sottoview


157

Ho ricevuto questo rapporto sull'arresto anomalo, ma non so come eseguirne il debug.

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 versione iOS è 7.0.3. Qualcuno ha vissuto questo strano incidente?

AGGIORNARE:

Non so dove nel mio codice ha causato questo arresto, quindi non posso pubblicare il codice qui, mi dispiace.

Secondo AGGIORNAMENTO

Vedi la risposta sotto.


3
Puoi mostrarci il tuo codice?
David Gölzhäuser,

43
Scusa ma non capisco la tua reazione eccessiva. L'errore dello stack è chiaro sul problema. Quindi, in primo luogo, puoi lasciare che l'utente inserisca più codice come richiesto (solo 1h la domanda posta e chiedi di chiuderlo immediatamente). In secondo luogo ho ricevuto un downvote senza motivo poiché la mia risposta è chiara. La domanda è "Qualcuno sperimenta questo strano incidente?". E ho detto perché ha ottenuto questo. Anche se non si trova specificamente nel suo codice.
Tancrede Chazallet,

9
Questa domanda è corretta. l'utente non può fornire il codice esatto di errore in questa situazione. perché non sa in quale view controller qualcosa vada storto
Ravindra Bagale,

16
Usiamo Crashlytics e abbiamo oltre 30 utenti che hanno bloccato la nostra app con "Impossibile aggiungere se stessi come sottoview", ovviamente non abbiamo codice che tenti di aggiungersi come sottoview. Dal backtrace non c'è alcun riferimento alla nostra app.
Richie Hyatt,

49
Votazione per riaprire; le persone che lo chiudono non fanno molto sviluppatore iOS, apparentemente, poiché questo è un problema comune introdotto da iOS7 e che uccide un sacco di app che andavano bene su iOS6 (l'ho visto su più progetti di diverse aziende). È un peccato che questa domanda abbia avuto un grande successo su Google, ma alcune persone miopi l'hanno chiusa.
Adam,

Risposte:


51

Sto ipotizzando sulla base di qualcosa di simile di cui ho eseguito il debug di recente ... se si spinge (o pop) un controller di visualizzazione con Animated: SÌ non si completa immediatamente e accadono cose brutte se si esegue un altro push o pop prima dell'animazione completata. Puoi facilmente verificare se questo è effettivamente il caso cambiando temporaneamente le tue operazioni Push e Pop in Animated: NO (in modo che si completino in modo sincrono) e vedendo se ciò elimina il crash. Se questo è davvero il tuo problema e desideri riattivare l'animazione, la strategia corretta è implementare il protocollo UINavigationControllerDelegate. Ciò include il seguente metodo, che viene chiamato al termine dell'animazione:

navigationController:didShowViewController:animated:

Fondamentalmente si desidera spostare un po 'di codice secondo necessità in questo metodo per assicurarsi che non si verifichino altre azioni che potrebbero causare una modifica allo stack di NavigationController fino a quando l'animazione non è terminata e lo stack è pronto per ulteriori modifiche.


Molto indietro su iOS 4, qualcosa che ho visto qualcosa di simile in una delle nostre app: IIRC, se facessi il pop-up animato e poi spingessi immediatamente animato, il codice dell'interfaccia utente verrebbe messo male. Finì per cambiare e non fare mai due operazioni push / pop animate schiena contro schiena. Ovviamente, da allora tutta la logica sottostante è stata riscritta, ma non è difficile credere che un bug simile non sia ancora presente.
Hot Licks

Ho avuto lo stesso problema. Nel mio caso ciò è accaduto perché l'app ha eseguito un'istruzione che modifica l' [newViewController setLabelTitle:...]interfaccia utente del nuovo view controller subito dopo aver chiamato pushViewController con Animated:YES.E ho risolto lo spostamento del metodo setLabelTitle per viewDidLoad sul nuovoViewController. Grazie per avermi dato la chiave.
jeprubio,

Sono contento che abbia aiutato! È bene sottolineare che anche lo spostamento del codice nel nuovo ViewController è un'opzione se sai quale classe sarà. Sempre più trovo utile catturare i vari metodi del protocollo UINavigationControllerDelegate, in ogni caso. E ho scoperto che negli eventi iOS8 si attivano in diversi ordini e alcune cose che erano più o meno sincrone ora tornano veloci ma programmano cose da fare in background in modo asincrono, creando molti nuovi bug di temporizzazione come questi. Grazie Apple!
RobP

14

Abbiamo anche iniziato a riscontrare questo problema e era molto probabile che la nostra fosse causata dallo stesso problema.

Nel nostro caso, in alcuni casi abbiamo dovuto estrarre i dati dal back-end, il che significava che un utente poteva toccare qualcosa e quindi ci sarebbe stato un leggero ritardo prima che si verificasse la spinta del nav. Se un utente stava rapidamente toccando, potrebbe finire con due push nav dallo stesso controller di visualizzazione, che ha innescato questa eccezione.

La nostra soluzione è una categoria su UINavigationController che impedisce push / pop a meno che il vc superiore sia lo stesso da un determinato momento.

File .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

File .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

Finora questo sembra aver risolto il problema per noi. Esempio:

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

Fondamentalmente, la regola è: prima che qualsiasi ritardo non correlato all'utente prenda un lucchetto dal relativo controller di navigazione e lo includa nella chiamata a push / pop.

La parola "blocco" può essere leggermente povera in quanto potrebbe insinuare che sta accadendo una qualche forma di blocco che ha bisogno di essere sbloccato, ma poiché non esiste un metodo di "sblocco" da nessuna parte, probabilmente va bene.

(Come sidenote, i "ritardi non correlati all'utente" sono eventuali ritardi che il codice sta causando, vale a dire qualcosa di asincrono. Gli utenti che toccano un controller di navigazione che viene spinto animatamente non contano e non è necessario eseguire il NavigationLock: versione per quelli casi).


Da quando hai detto che stavi provando questa soluzione, hai risolto il problema per te?
Mike D,

Finora si. Il problema non è riemerso. Aggiornerò la risposta.
Kalle

4
Ho usato una versione modificata basata sulla tua: gist.github.com/mdewolfe/9369751 . Sembra che l'abbia corretto.
Mike D

2
@Kalle Questa soluzione funziona per push / pop. Ma come risolvere questo errore se uso segue?
Geek

@Kadle Puoi aiutarmi a implementarlo? Guardate stackoverflow.com/q/23247713/1323014 THX
Marckaraujo

12

Questo codice risolve il problema: https://gist.github.com/nonamelive/9334458

Utilizza un'API privata, ma posso confermare che è sicuro sull'App Store. (Una delle mie app che utilizzano questo codice è stata approvata dall'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;
}

Finora questa è stata la soluzione migliore, con alcuni degli altri avrei ancora avuto il problema del doppio push in modo casuale o avrei avuto un controller di navigazione bloccato.
blueice

Questo codice non viene compilato per me, manca qualcosa?
Maxime B,

8

Descriverò ulteriori dettagli su questo arresto anomalo nella mia app e contrassegnerò come risposta.

La mia app ha un UINavigationController con il controller di root è un UITableViewController che contiene un elenco di oggetti nota. L'oggetto note ha una proprietà content in html. Selezionare una nota andrà al controller dettagli.

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

Controller di dettaglio

Questo controller ha un UIWebView, visualizza il contenuto della nota passato dal controller di root.

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

Questo controller è il delegato del controllo webview. Se la nota contiene collegamenti, toccare un collegamento per accedere al browser Web in-app.

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

Ho ricevuto il rapporto sugli arresti anomali sopra riportato ogni giorno. Non so dove nel mio codice ha causato questo crash. Dopo alcune ricerche con l'aiuto di un utente, sono stato finalmente in grado di risolvere questo crash. Questo contenuto html causerà l'arresto anomalo:

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

Nel metodo viewDidLoad del controller dettagli, ho caricato questo html nel controllo webview, subito dopo, il metodo delegato sopra è stato chiamato immediatamente con request.URL è l'origine dell'iframe (google.com). Questo metodo delegato chiama il metodo pushViewController mentre è in viewDidLoad => crash!

Ho risolto questo arresto verificando il tipo di navigazione:

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

Spero che questo ti aiuti


1
Non sarebbe una buona opzione spingere il controller senza animazione quando viene chiamato viewDidLoad?
Rivera,

6

Ho avuto lo stesso problema, quello che ha funzionato semplicemente per me è stato cambiare Animated: Sì in Animated: No.

Sembra che il problema sia dovuto al fatto che l'animazione non si è completata in tempo.

Spero che questo aiuti qualcuno.


3

Per riprodurre questo errore, prova a spingere due controller di visualizzazione contemporaneamente. O spingendo e facendo lo stesso pop. Esempio:

inserisci qui la descrizione dell'immagine Ho creato una categoria che intercetta queste chiamate e le rende sicure assicurandomi che non ci siano altre spinte mentre si è in corso. Basta copiare il codice nel tuo progetto e, a causa del metodo che sfrigola, sarai a posto.

#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 questa soluzione è che se chiami popToRootViewControllero popToViewController:quando sei già sul controller della vista root o sul viewController verrà visualizzato, didShowViewControllernon verrà chiamato e verrà bloccato viewTransitionInProgress.
divergio,

1
Puoi spiegare queste righe: self.interactivePopGestureRecognizer.delegate = (id<UIGestureRecognizerDelegate>)viewController; [self.interactivePopGestureRecognizer setEnabled:YES]; quando il riconoscimento è stato disabilitato? E come fai a sapere quale dovrebbe essere il delegato? Con quelle righe dentro, per me rompe il gesto pop dopo essere saltato fuori una volta.
divergio

Ho provato a implementarlo e dopo un po 'si blocca il controller di navigazione, probabilmente a causa di ciò che ha detto @divergio.
blueice

2

Ho appena riscontrato questo problema. Lascia che ti mostri il mio codice:

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)
 }

L'errore si presenta a causa di questa riga:

firstView.addSubView(firstView)

Non è possibile aggiungere se stessi alla visualizzazione secondaria. Ho cambiato la riga di codice in:

firstView.addSubView(secondView)

L'errore è scomparso e sono stato in grado di vedere entrambi i punti di vista. Ho pensato che questo avrebbe aiutato chiunque volesse vedere un esempio.


Ho anche provato questo approccio, ma lo stacktrace sarebbe diverso e mostrerebbe effettivamente la riga del tuo codice che causava il crash. Credo che il problema della fonte sia diverso dalla domanda.
Ben

1

Cerca il tuo codice per "addSubview".

In uno dei posti in cui hai chiamato questo metodo hai provato ad aggiungere una vista al suo array di viste secondarie usando questo metodo.

Per esempio:

[self.view addSubview:self.view];

O:

[self.myLabel addSubview:self.myLabel];

Felice di sentire che hai trovato il tuo errore e ora capisco esattamente perché hai ricevuto il messaggio "Impossibile aggiungere se stesso come sottoview". In un punto in cui View2 era il controller della vista principale del controller di navigazione, è stato premuto View2 che ha causato ciò: [View2.view addSubview:View2.view]quindi, aggiungendo self come subview.
Michal Shatz,

1

Penso che spingere / fare scoppiare i controller di visualizzazione con l'animazione in qualsiasi momento dovrebbe andare perfettamente bene e l'SDK dovrebbe gestire con gentilezza la coda di chiamate per noi.

Quindi non lo fa e tutte le soluzioni cercano di ignorare i push successivi, che potrebbero essere considerati un bug poiché lo stack di navigazione finale non è quello che intendeva il codice.

Ho invece implementato una coda di chiamate push:

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

Non gestisce le code miste di push e pop ma è un buon inizio per risolvere la maggior parte dei nostri arresti anomali.

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


Ho provato la tua soluzione che sembra molto buona, ma sto riscontrando un problema. Quando spingo 2 controller di visualizzazione con NO animato uno dopo l'altro, vedo brevemente il primo. Non è successo prima. Qualche idea su cosa posso fare per risolverlo?
Jan

Sto cercando di costruire un progetto che può causare costantemente questo tipo di crash (il mio progetto reale riceve rapporti sugli arresti anomali come questo). Ho fatto una semplice app con un controller di navigazione, un controller di root e un pulsante che spinge immediatamente 4 nuovi controller di visualizzazione nello stack di navigazione e quindi elimina l'ultimo. Senza alcuna sottoclasse speciale o nulla sembra effettivamente funzionare bene. Apple ha risolto questo problema di recente?
Cruinh,

1

Scusate il ritardo per la festa. Di recente ho avuto questo problema in cui la mia barra di navigazione entra in stato danneggiato a causa della spinta di più di un controller di visualizzazione contemporaneamente. Ciò accade perché l'altro controller della vista viene premuto mentre il primo controller della vista è ancora in fase di animazione. Prendendo spunto dalla risposta non lunatica, mi è venuta in mente la mia semplice soluzione che funziona nel mio caso. Devi solo sottoclassare UINavigationControllere sovrascrivere il metodo pushViewController e verificare se l'animazione del controller della vista precedente è ancora terminata. Puoi ascoltare il completamento dell'animazione rendendo la tua classe un delegato UINavigationControllerDelegatee impostando il delegato suself .

Ho caricato una sintesi qui per rendere le cose semplici.

Assicurati di impostare questa nuova classe come NavigationController nello storyboard.


Finora sembra aver corretto gli arresti anomali dell'app su cui stavo lavorando ... inoltre, la soluzione è piuttosto semplice e chiara sul punto: la prima animazione del viewcontroller non era ancora completa. Le persone che hanno lo stesso problema dovrebbero verificarlo.
alasker,

0

Sulla base del grande suggerimento di @RobP, ho creato la sottoclasse UINavigationController per evitare tali problemi. Gestisce la spinta e / o lo scoppio e puoi eseguire in sicurezza:

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

Se 'acceptConflictingCommands' lo contrassegna come vero (per impostazione predefinita) l'utente vedrà il push animato di vc1, vc2, vc3 e quindi vedrà il pop-up animato di vc3. Se 'acceptConflictingCommands' è falso, tutte le richieste push / pop verranno scartate fino a quando vc1 non verrà completamente inviato, quindi verranno eliminate le altre 3 chiamate.


quei comandi sono effettivamente in conflitto? Ho appena messo insieme un nuovo progetto rapido per vedere accadere questo incidente, usando il codice come sopra (ma in Swift), e in realtà ha eseguito ogni push e il pop, tutto in sequenza. uno dopo l'altro. Nessun incidente. Senza utilizzare alcuna sottoclasse. solo il normale UINavigationController di Apple.
Cruinh,

Si stava effettivamente bloccando con ObjC e iOS 7. Non posso confermare se si verifica ancora adesso. Sei sicuro di eseguire i comandi con animated:trueflag?
hris.to

Sì, stavo usando la bandiera animata: vera.
Cruinh,



0

A volte hai erroneamente provato ad aggiungere una vista alla sua vista.

halfView.addSubview(halfView)

cambia questo alla tua vista secondaria.

halfView.addSubview(favView)

0

Ho riscontrato anche questo problema. Quando ho eseguito l'analisi del registro di Firebase, ho riscontrato che questo problema si verifica solo quando l'app è avviata a freddo. Quindi ho scritto una demo in grado di riprodurre questo incidente.

.

Ho anche scoperto che quando viene visualizzato il viewcontroller principale della finestra, l'esecuzione di più push non causerà nuovamente lo stesso problema. (È possibile commentare testColdStartUp (rootNav) in AppDelegate.swift e rimuovere il commento dal commento testColdStartUp () in ViewController.swift)

ps: ho analizzato la scena di questo incidente nella mia app. Quando l'utente fa clic sulla notifica push per avviare a freddo l'app, l'app si trova ancora nella pagina di avvio e fa clic su un'altra push per saltare. Al momento, l'app potrebbe apparire Crash. La mia attuale soluzione è memorizzare nella cache il push o l'avvio a freddo del collegamento universale per aprire la pagina di salto dell'app, attendere la visualizzazione del rootviewcontroller e quindi ritardare l'esecuzione.


-2

prova la tua navigazione usando il metodo del ritardo, per completare l'ultima animazione di navigazione,

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


-2

Una vista non può essere aggiunta come sottoview in se stessa.

Le viste mantengono una gerarchia genitore-figlio, quindi se aggiungi una vista come sottoview in se stessa, sarà un'eccezione.

se una classe è UIViewController, per ottenere la sua vista usi self.view.

se una classe è UIView Class, per ottenerne la vista usi self.


-3

non è possibile aggiungere se stessi come sottoview se si tratta di una classe UiViewController. puoi aggiungere self come sottoview se sarà una classe UiView.


-9

Se desideri aggiungere una vista secondaria a una vista, puoi farlo in questo modo;

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

Ben fatto, è un bel pezzo di codice. Ora puoi dirmi cosa c'entra questo con questa domanda e come risolverlo?
Popeye,

@Popeye Hai un'idea migliore?
David Gölzhäuser,

No, perché non hanno fornito informazioni / codice sufficienti per replicare il problema. Quindi non è possibile rispondere a questa domanda, sembra proprio che tu stia dicendo loro come fare qualcosa che non ha nulla a che fare con il loro problema.
Popeye,

2
Immagino sia stato molto più utile per loro semplicemente chiudere il problema. Fatto! +1 per David G per aver effettivamente cercato di aiutare qualcuno su StackOverflow. Vorrei poter -1 i tuoi voti stretti !!! Questo sta ancora accadendo per gli utenti e potrebbe essere un bug in iOS7 per tutto ciò che sappiamo. Quindi, solo perché qualcuno non può pubblicare il codice offensivo non significa che la domanda non sia valida e utile per essere vista da altri utenti. Anche se è solo per vedere che altre persone stanno vedendo lo stesso problema senza alcun motivo logico per cui lo stanno vedendo. -rrh
Richie Hyatt
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.