Erro no aplicativo iOS - Não é possível adicionar a si mesmo como subvisão


157

Recebi este relatório de falha, mas não sei como depurá-lo.

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

A versão do iOS é 7.0.3. Alguém experimentou esse acidente estranho?

ATUALIZAR:

Não sei onde no meu código causou essa falha, por isso não posso postar o código aqui, desculpe.

Segunda ATUALIZAÇÃO

Veja a resposta abaixo.


3
Você pode nos mostrar seu código?
David Gölzhäuser

43
Desculpe, mas eu não entendo sua reação exagerada. O erro de pilha está claro no problema. Então, primeiro, você pode permitir que o usuário coloque mais código conforme solicitado (apenas 1 h da pergunta e você pede para fechá-lo imediatamente). Segundo, recebi um voto negativo sem motivo, pois minha resposta é clara. A pergunta é "Alguém experimentou esse acidente estranho?". E eu disse por que ele conseguiu isso. Mesmo que não esteja localizado especificamente em seu código.
Tancrede Chazallet

9
Esta questão é correta. O usuário não pode fornecer o código exato de erro nessa situação. porque ele não sabe em que visualizar controlador de algo dar errado
Ravindra Bagale

16
Usamos o Crashlytics e temos mais de 30 usuários que travaram nosso aplicativo com a opção "Não é possível adicionar auto como subview", é claro que não temos código que tenta se adicionar como subview. No backtrace, não há nenhuma referência ao nosso aplicativo.
Richie Hyatt

49
Votação para reabrir; aparentemente, as pessoas que o fecham não desenvolvem muito o iOS, já que esse é um problema comum introduzido pelo iOS7 e mata um monte de aplicativos que estavam bem no iOS6 (eu já vi isso em vários projetos de diferentes empresas). É uma pena que esta questão seja um dos principais problemas no Google, mas algumas pessoas míopes a fecharam.
Adam

Respostas:


51

Estou especulando com base em algo semelhante que depurei recentemente ... se você enviar (ou abrir) um controlador de exibição com o Animated: SIM, ele não será concluído imediatamente, e coisas ruins acontecerão se você fizer outro envio ou envio antes da animação completa. Você pode facilmente testar se esse é realmente o caso, alterando temporariamente suas operações Push e Pop para Animated: NO (para que elas sejam concluídas de forma síncrona) e ver se isso elimina a falha. Se esse é realmente o seu problema e você deseja ativar a animação novamente, a estratégia correta é implementar o protocolo UINavigationControllerDelegate. Isso inclui o seguinte método, chamado após a conclusão da animação:

navigationController:didShowViewController:animated:

Basicamente, você deseja mover algum código conforme necessário para esse método para garantir que nenhuma outra ação que possa causar uma alteração na pilha NavigationController ocorra até que a animação seja concluída e a pilha esteja pronta para mais alterações.


No passado, sobre o iOS 4 - algo que eu vi algo semelhante em um de nossos aplicativos - IIRC, se você aparecesse animado e depois o empurrasse imediatamente, o código da interface do usuário ficaria muito estragado. Acabou mudando para nunca fazer duas operações push / pop animadas consecutivas. Obviamente, toda a lógica subjacente foi reescrita desde então, mas não é difícil acreditar que um bug semelhante ainda não esteja lá.
Hot Licks

Eu tive o mesmo problema. No meu caso, isso aconteceu porque o aplicativo executou uma instrução que altera a interface do [newViewController setLabelTitle:...]usuário do novo controlador de exibição logo após chamar pushViewController com Animated:YES.E resolvi mover o método setLabelTitle para viewDidLoad no newViewController. Obrigado por me dar a pista.
jeprubio 6/11/14

Fico feliz que ajudou! É bom ressaltar que mover o código para o novo ViewController também é uma opção se você souber qual classe será. Cada vez mais, acho útil capturar os vários métodos do protocolo UINavigationControllerDelegate, em qualquer caso. E descobri que, no iOS8, os eventos disparam em ordens diferentes, e algumas coisas que costumavam ser mais ou menos síncronas agora retornam rapidamente, mas agendam coisas para serem executadas em segundo plano de forma assíncrona, criando muitos novos bugs de tempo como esses. Obrigado Apple!
RobP

14

Também começamos a solucionar esse problema, e era muito provável que as nossas fossem causadas pelo mesmo problema.

Em nosso caso, tivemos que extrair dados do back-end em alguns casos, o que significava que um usuário poderia tocar em algo e, em seguida, haveria um pequeno atraso antes da ocorrência do push de navegação. Se um usuário estivesse digitando rapidamente, ele poderia acabar com dois push de navegação do mesmo controlador de exibição, o que desencadeou essa mesma exceção.

Nossa solução é uma categoria no UINavigationController que evita empurrões / pops, a menos que o vc superior seja o mesmo em um determinado momento.

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

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

Até agora, isso parece ter resolvido o problema para nós. Exemplo:

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

Basicamente, a regra é: antes de qualquer atraso não relacionado ao usuário pegue um bloqueio do controlador de navegação relevante e inclua-o na chamada para push / pop.

A palavra "bloqueio" pode ter uma formulação um pouco ruim, pois pode indicar que há alguma forma de bloqueio que precisa ser desbloqueada, mas como não há um método de "desbloqueio" em qualquer lugar, provavelmente tudo bem.

(Como uma nota lateral, "atrasos não relacionados ao usuário" são quaisquer atrasos que o código está causando, ou seja, qualquer coisa assíncrona. Os usuários que tocam em um controlador nav que é animado animadamente não contam e não é necessário executar o navigationLock: version para esses casos).


Desde que você disse que estava tentando esta solução, ela resolveu o problema?
Mike D

Até agora sim. O problema não ressurgiu. Vou atualizar a resposta.
Kalle

4
Eu usei uma versão modificada com base na sua: gist.github.com/mdewolfe/9369751 . Parece que foi corrigido.
Mike D

2
@ Kalle Esta solução funciona para push / pop. Mas como resolver esse erro se eu usar segue?
21714 Geek

@Kadle Você pode me ajudar a implementar isso? Veja stackoverflow.com/q/23247713/1323014 THX
Marckaraujo

12

Este código resolve o problema: https://gist.github.com/nonamelive/9334458

Ele usa uma API privada, mas posso confirmar que é seguro na App Store. (Um dos meus aplicativos usando esse código foi aprovado pela 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 tem sido a melhor solução até agora, com algumas das outras eu ainda obteria aleatoriamente o problema de envio duplo ou um controlador de navegação congelado.
blueice

Esse código não é compilado para mim, algo está faltando?
Maxime B

8

Descreverei mais detalhes sobre essa falha no meu aplicativo e marcarei isso como respondido.

Meu aplicativo tem um UINavigationController com o controlador raiz. É um UITableViewController que contém uma lista de objetos de anotação. O objeto de nota possui uma propriedade de conteúdo em html. Selecione uma nota para o controlador de detalhes.

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

Controlador de detalhes

Este controlador possui um UIWebView, exibe o conteúdo da nota passado no controlador raiz.

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

Este controlador é o delegado do controle de visualização na web. Se a nota contiver links, toque em um link para o navegador do aplicativo.

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

Recebi o relatório de falha acima todos os dias. Não sei onde no meu código causou essa falha. Depois de algumas investigações com a ajuda de um usuário, finalmente pude consertar essa falha. Este conteúdo html causará a falha:

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

No método viewDidLoad do controlador de detalhes, carreguei esse html no controle webview, logo depois, o método delegado acima foi chamado imediatamente com request.URL é a fonte do iframe (google.com). Este método delegado chama o método pushViewController enquanto em viewDidLoad => travamento!

Corrigi essa falha verificando o navigationType:

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

Espero que isto ajude


1
Não seria uma boa opção empurrar o controlador sem animação quando chamado viewDidLoad?
Rivera

6

Eu tive o mesmo problema, o que simplesmente funcionou para mim foi mudar Animated: Yes para Animated: No.

Parece que o problema ocorreu devido à animação não ser concluída a tempo.

Espero que isso ajude alguém.


3

Para reproduzir esse bug, tente pressionar dois controladores de exibição ao mesmo tempo. Ou empurrando e aparecendo ao mesmo tempo. Exemplo:

insira a descrição da imagem aqui Eu criei uma categoria que intercepta essas chamadas e as torna seguras, certificando-se de que nenhum outro push esteja acontecendo enquanto um estiver em andamento. Basta copiar o código no seu projeto e, devido ao método swizzling, você estará pronto.

#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

Um problema com esta solução é que, se você ligar popToRootViewControllerou popToViewController:quando já estiver no controlador de exibição raiz ou no viewController, ele didShowViewControllernão será chamado e ficará preso viewTransitionInProgress.
divergio

1
Você pode explicar estas linhas: self.interactivePopGestureRecognizer.delegate = (id<UIGestureRecognizerDelegate>)viewController; [self.interactivePopGestureRecognizer setEnabled:YES]; Quando o reconhecedor foi desativado? E como você sabe o que o delegado deve ser? Com essas falas, para mim, ele quebra o gesto pop depois de aparecer uma vez.
divergio

Eu tentei implementar isso e depois de um tempo ele trava o controlador de navegação, provavelmente devido ao que o @divergio mencionou.
blueice

2

Acabei de experimentar esse problema também. Deixe-me mostrar meu 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)
 }

O erro surge devido a esta linha:

firstView.addSubView(firstView)

Você não pode adicionar auto à subview. Alterei a linha de código para:

firstView.addSubView(secondView)

O erro foi resolvido e eu pude ver as duas visualizações. Só pensei que isso ajudaria quem quisesse ver um exemplo.


Eu também tentei essa abordagem, mas o stacktrace seria diferente e realmente mostraria a linha do seu código causando a falha. Acredito que o problema da fonte seja diferente da questão.
Ben

1

Pesquise seu código por "addSubview".

Em um dos lugares que você chamou esse método, você tentou adicionar uma exibição à sua própria matriz de sub-exibições usando esse método.

Por exemplo:

[self.view addSubview:self.view];

Ou:

[self.myLabel addSubview:self.myLabel];

É um prazer saber que você encontrou o seu erro e agora entendo exatamente por que você recebeu a subvisão "Não é possível adicionar a si mesmo como subview". Em um ponto em que o seu View2 era o controlador de visualização raiz do seu controlador de navegação, você pressionou o View2, o que causou isso: [View2.view addSubview:View2.view]adicionando self como subview.
Michal Shatz

1

Eu acho que empurrar / abrir os controladores de exibição com animação a qualquer momento deve estar perfeitamente bem e o SDK deve lidar graciosamente com a fila de chamadas para nós.

Portanto, isso não ocorre e todas as soluções tentam ignorar os empurrões subsequentes, o que pode ser considerado um bug, já que a pilha de navegação final não é o que o código pretendia.

Eu implementei uma fila de chamadas 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

Ele não lida com filas mistas de push e pop, mas é um bom começo para corrigir a maioria de nossas falhas.

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


Estou tentando sua solução, que parece muito boa, mas estou tendo um problema. Ao pressionar 2 controladores de exibição com NENHUM animado um após o outro, vejo brevemente o primeiro. Isso não aconteceu antes. Alguma idéia do que eu posso fazer para consertar isso?
Jan

Estou tentando criar um projeto que possa causar consistentemente esse tipo de falha (meu projeto real recebe relatórios de falhas como esse). Eu fiz um aplicativo simples com um controlador de navegação, um controlador raiz e um botão que pressiona imediatamente quatro novos controladores de exibição na pilha de navegação e depois abre o último. Sem nenhuma subclasse especial ou qualquer coisa, ela realmente parece funcionar bem. A Apple corrigiu isso recentemente?
Cruinh

1

Desculpe por estar atrasado para a festa. Recentemente, tive um problema em que minha barra de navegação entra em estado corrompido por pressionar mais de um controlador de exibição ao mesmo tempo. Isso acontece porque o outro controlador de exibição é pressionado enquanto o primeiro controlador de exibição ainda está animado. Tomando dica da resposta não-elástica, vim com minha solução simples que funciona no meu caso. Você só precisa subclassificar UINavigationControllere substituir o método pushViewController e verificar se a animação anterior do controlador de exibição ainda está concluída. Você pode ouvir a conclusão da animação transformando sua classe em um delegado UINavigationControllerDelegatee definindo-o como self.

Fiz upload de uma essência aqui para simplificar as coisas.

Apenas certifique-se de definir esta nova classe como o NavigationController no seu storyboard.


Até agora, parece ter corrigido as falhas no aplicativo em que eu estava trabalhando ... além disso, a solução é bem direta e clara: a primeira animação do controlador de exibição ainda não estava concluída. Pessoas com o mesmo problema devem verificar isso.
Alasker

0

Com base na ótima dica do @RobP, criei a subclasse UINavigationController para evitar esses problemas. Ele lida com push e / ou popping e você pode executar com segurança:

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

Se o sinalizador 'acceptConflictingCommands' for verdadeiro (por padrão), o usuário verá o envio animado de vc1, vc2, vc3 e, em seguida, o envio animado de vc3. Se 'acceptConflictingCommands' for falso, todas as solicitações push / pop serão descartadas até que vc1 seja totalmente pressionado - portanto, outras 3 chamadas serão descartadas.


esses comandos são realmente conflitantes? Acabei de criar um projeto novo e rápido para ver essa falha acontecer, usando código como o descrito acima (mas em Swift), e ele realmente executava cada push e o pop, tudo em sequência. um após o outro. Nenhum acidente. Sem usar nenhuma subclasse. apenas o UINavigationController regular da apple.
Cruinh 26/08/2015

De fato, estava travando com o ObjC e o iOS 7. Não posso confirmar se ainda ocorre agora. Tem certeza de que está executando os comandos com animated:truesinalizador?
precisa saber é o seguinte

Sim, eu estava usando a bandeira animated: true.
Cruinh 22/09/2015

0

A solução da Nonamelive é incrível. Mas, se você não quiser usar a API privada, poderá apenas obter o UINavigationControllerDelegatemétodo ou alterar a animação YESpara NO. Aqui está um exemplo de código, você pode herdar. Espero que seja útil :)

https://github.com/antrix1989/ANNavigationController



0

Às vezes, você tentou erroneamente adicionar uma exibição à sua própria exibição.

halfView.addSubview(halfView)

altere isso para sua sub-visualização.

halfView.addSubview(favView)

0

Eu também encontrei esse problema. Quando fiz a análise de log do Firebase, descobri que esse problema ocorre apenas quando o aplicativo é iniciado a frio. Então, eu escrevi uma demonstração que pode reproduzir essa falha.

.

Também descobri que, quando o controlador de exibição raiz da janela é exibido, executar vários toques não causa o mesmo problema novamente. (Você pode comentar testColdStartUp (rootNav) em AppDelegate.swift e descomentar o comentário testColdStartUp () em ViewController.swift)

ps: analisei a cena desse acidente no meu aplicativo. Quando o usuário clica na notificação push para iniciar o aplicativo a frio, ele ainda está na página Iniciar e clica em outro push para pular. No momento, o aplicativo pode aparecer o Crash. Meu atual A solução é armazenar em cache o push ou o link universal a frio para abrir a página de salto do aplicativo, aguardar a exibição do controlador rootview e atrasar a execução.


-2

tente sua navegação usando o método delay, para concluir a última animação de navegação,

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


-2

Uma visualização não pode ser adicionada como uma subvisão nela mesma.

As visualizações mantêm uma hierarquia pai-filho; portanto, se você incluir uma visualização como uma subvisão em si, será por exceção.

se uma classe for UIViewController, para obter sua visualização, use self.view.

se uma classe é UIView Class, para obter sua visão, você usa-se.


-3

você não pode adicionar self como subview, se for uma classe UiViewController. você pode adicionar self como subview, se for uma classe UiView.


-9

Se você deseja adicionar uma Subview a uma View, pode fazê-lo assim;

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

Bem feito, é um bom pedaço de código. Agora você pode me dizer o que isso tem a ver com esta pergunta e como ela a resolve?
Popeye

@Popeye Você tem uma ideia melhor?
David Gölzhäuser 24/10

Não, porque eles não forneceram informações / códigos suficientes para replicar o problema. Portanto, não há como isso ser respondido, apenas parece que você está dizendo a eles como fazer algo que não tem nada a ver com o problema deles.
Popeye

2
Acho que foi muito mais útil para eles fechar o problema. Entendi! +1 para David G por realmente tentar ajudar alguém no StackOverflow. Eu gostaria de poder -1 seus votos próximos !!! Isso ainda está acontecendo para os usuários e pode ser um bug no iOS7, pelo que sabemos. Portanto, apenas porque alguém não pode postar o código incorreto não significa que a pergunta não é válida e valiosa para outros usuários verem. Mesmo que seja apenas para ver que outras pessoas estão vendo o mesmo problema sem nenhuma razão lógica para vê-lo. -rrh
Richie Hyatt
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.