web-dev-qa-db-fra.com

l'application iOS se bloque sur PushViewController

Mon contrôleur de navigation gèlera par intermittence lors du Push. Il semble ajouter le nouveau contrôleur de vue sur la pile, mais l'animation n'a jamais lieu. J'ai également deux autres conteneurs qui contiennent des contrôleurs de vue à l'écran, et je peux très bien interagir avec les deux après le gel du contrôleur de navigation. La chose vraiment intéressante est que si j'essaie de placer un autre contrôleur de vue sur la pile du contrôleur de navigation, j'ai remarqué qu'il y avait un contrôleur de vue supplémentaire au-dessus de la pile (le contrôleur de vue que j'ai poussé initialement qui a gelé le contrôleur de navigation). Donc, si je suis sur l'écran d'accueil (nous l'appellerons VC-Home) et que j'essaie de pousser une nouvelle vue (VC-1) et que cela se fige, j'essaie alors de pousser une nouvelle vue (VC-2), Voici ce que je vois dans la pile actuelle avant le Push:

{ [VC-Home, VC-1] }

et après l’appel de pushViewController, il reste le même; VC-2 n'est pas ajouté à la pile.

D'après ce que je peux dire, le contrôleur de navigation commence l'animation en rendant le contrôleur de vue précédent inactif avant le début de l'animation, mais l'animation n'a jamais lieu, laissant le contrôleur de navigation dans un état figé. 

Je crée le nouveau contrôleur de vue à partir d'un scénario en appelant UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("ViewController"). Je ne pense donc pas qu'il y ait de problèmes. Je ne remplace pas non plus pushViewController dans la barre de navigation. Une particularité unique de mon application réside dans le fait qu’elle est très chargée en images haute résolution (avec SDWebImage pour le gérer) et que j’ai toujours trois conteneurs à l’écran à la fois (un contrôleur de navigation, un contrôleur de vue pour la recherche et un goutteur interactif menu latéral/menu coulissant). 

L’utilisation du processeur est faible et l’utilisation de la mémoire est normale (autour de 60-70 Mo lorsque le périphérique se bloque).

Existe-t-il des idées sur ce qui pourrait être à l'origine de ce problème ou des astuces de débogage qui pourraient m'aider à découvrir le vrai problème?

Mettre à jour

Il n'y a pas de code unique pour UINavigationController puisque je ne fais que pousser à l'aide de pushViewController (). Voici le code qui l'appelle:

func didSelectItem(profile: SimpleProfile) {
     let vc = UIStoryboard.profileViewController()
     vc.profile = profile
     navigationController?.pushViewController(vc, animated: true)
}

Le ViewController que j'ai poussé a le code suivant dans viewDidLoad:

override func viewDidLoad() {
    super.viewDidLoad()
    button.roundView()

    if let type = profile?.profileType {
        //load multiple view controllers into a view pager based on type 
        let viewControllers = ProfileTypeTabAdapter.produceViewControllersBasedOnType(type)
        loadViewPagerViews(viewControllers)

        let topInset = headerView.bounds.height + tabScrollView.contentSize.height
        if let viewPager = viewPager {
            for view in viewPager.views {
                if let tempView = view as? PagingChildViewController {
                    tempView.profile = fullProfile
                    tempView.parentVCDelegate = self
                    tempView.topInset = topInset
                }
            }
        }
    }
}

func loadViewPagerViews(viewControllers: [UIViewController]) {
    viewPager?.views = viewControllers
    viewPager?.delegate = self

    //loading views into paging scroll view (using PureLayout to create constraints)
    let _ = subviews.map { $0.removeFromSuperview() }
    var i = 0
    for item in views {
        addSubview(item.view)
        item.view.autoSetDimensionsToSize(CGSize(width: tabWidth, height: tabHeight))
        if i == 0 {
            item.view.autoPinEdgeToSuperviewEdge(.Leading)
        } else if let previousView = views[i-1].view {
            item.view.autoPinEdge(.Leading, toEdge: .Trailing, ofView: previousView)
        }
        if i == views.count {
            item.view.autoPinEdgeToSuperviewEdge(.Trailing)
        }
        i += 1
    }

    contentSize = CGSize(width: Double(i)*Double(tabWidth), height: Double(tabHeight))
}

Mise à jour 2

Je l'ai enfin réussi à geler à nouveau. L'application était en arrière-plan et je l'ai ramenée et j'ai essayé de placer un contrôleur de vue sur la pile lorsqu'il s'est figé. J'ai remarqué qu'une animation avait lieu. J'ai un scrollview en haut de la page qui parcourt son contenu toutes les 10 secondes (pensez à la bannière supérieure des magasins d'applications). Sur ce gel, j'ai remarqué que la bannière était en pleine animation. 

Voici la fonction de défilement de my UIScrollView appelée toutes les 10 secondes:

func moveToNextItem() {
    let pageWidth: CGFloat = CGRectGetWidth(frame)
    let maxWidth: CGFloat = pageWidth * CGFloat(max(images.count, profileImages.count))
    let contentOffset: CGFloat = self.contentOffset.x
    let slideToX = contentOffset + pageWidth

    //if this is the end of the line, stop the timer
    if contentOffset + pageWidth == maxWidth {
        timer?.invalidate()
        timer = nil
        return
    }

    scrollRectToVisible(CGRectMake(slideToX, 0, pageWidth, CGRectGetHeight(frame)), animated: true)
}

Je ne me souviens pas d'avoir eu un arrêt Push à cause d'une animation/défilement en cours, mais je peux me tromper.

J'ai également revérifié la pile et la situation décrite ci-dessus est toujours celle où [VC-Home, VC-1] est la pile et que VC-2 n'est pas enfoncé. J'ai également passé en revue les variables de VC-1 et tout est chargé (appels de données et images chargées).

Mise à jour 3

Cela devient étranger à la seconde. J'ai écrasé pushViewController pour pouvoir y placer un point d'arrêt et effectuer un débogage basé sur la réponse d'Alessandro Ornano. Si je ne réussis pas à pousser un contrôleur de vue, puis envoie mon application en arrière-plan, place un point d'arrêt dans l'appel pushViewController et ramène l'application, le point d'arrêt est immédiatement atteint plusieurs fois. Si je continue ensuite après tous les hits, le contrôleur de vue suivant devient soudainement visible et le dernier contrôleur de vue que j'ai essayé de pousser est maintenant sur la pile en tant que dernier contrôleur de vue. Cela signifie que celui que je vois est toujours désactivé, ce qui me met essentiellement dans la même position qu'auparavant.

19
Maxwell

Nous avons rencontré le même problème il y a quelques semaines. Et pour notre problème, nous l'avons réduit à left-Edge pop gesture recogniser. Vous pouvez essayer de vérifier si vous pouvez reproduire ce problème en suivant les étapes ci-dessous.

  • Essayez d’utiliser le geste de pop Edge gauche lorsque aucun contrôleur de vue ne se trouve en dessous (c’est-à-dire sur les contrôleurs de vue racine, votre contrôleur VC-Home).
  • Essayez de cliquer sur n'importe quel élément de l'interface utilisateur après cela.

Si vous parvenez à reproduire le gel, essayez de désactiver la interactivePopGestureRecognizer lorsque la pile de contrôleurs de vue ne comporte qu'un seul contrôleur de vue.

Reportez-vous à this question pour plus de détails. Vous trouverez ci-dessous le code du lien pour faciliter la consultation.

- (void)navigationController:(UINavigationController *)navigationController
   didShowViewController:(UIViewController *)viewController
                animated:(BOOL)animate
{
    if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)])
{
        if (self.viewControllers.count > 1)
        {
            self.interactivePopGestureRecognizer.enabled = YES;
        }
        else
        {
            self.interactivePopGestureRecognizer.enabled = NO;
        }
    }
}
35
Penkey Suresh

Great answer by @Penkey Suresh! Sauvé ma journée! Voici une version Swift 3 avec un petit ajout qui a fait la différence pour moi:

    func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {


    if (navigationController.viewControllers.count > 1)
    {
         self.navigationController?.interactivePopGestureRecognizer?.delegate = self
        navigationController.interactivePopGestureRecognizer?.isEnabled = true;
    }
    else
    {
         self.navigationController?.interactivePopGestureRecognizer?.delegate = nil
        navigationController.interactivePopGestureRecognizer?.isEnabled = false;
    }
}

N'oubliez simplement pas d'ajouter UINavigationControllerDelegate et de définir le navigationController?.delegate = self Une autre partie importante consiste à affecter interactivePopGestureRecognizer à self ou à nil en conséquence.

5
Tim Friedland

Je pensais à congélation intermittente _, fil principal et SDWebImage .

En supposant que vous utilisiez l'image que vous avez téléchargée à partir de downloadImageWithURL: options: progress: completed:le bloc terminé _ .. si c'est le cas, assurez-vous de l'envoyer dans la file d'attente principale avant d'utiliser l'image.

Si vous utilisez directement SDWebImageDownloader, le bloc d'achèvement (comme vous l'avez indiqué) sera appelé sur une file d'attente en arrière-plan, vous pouvez le réparer à l'aide de dispatch_async dans la file d'attente principale à partir de l'achèvement. 

Sinon, vous pouvez utiliser: SDWebImageManager downloadImageWithURL: options: progression: terminé: (méthode qui appelle les blocs d'achèvement de la file d'attente principale).

si le problème persiste (juste parce que vous parlez de "..des choses uniques à propos de mon application, c'est qu'elle est très chargée en image ..") regardez également Problèmes courants , notamment le Gestion de l'actualisation de l'image know problèmes.

Ajoutez à votre chèque également ce code de code Nice:

import UIKit.UINavigationController

public typealias VoidBlock = (Void -> Void)

public extension UINavigationController  
{
    public func pushViewController(viewController: UIViewController, animated: Bool, completion: VoidBlock) {
        CATransaction.begin()
        CATransaction.setCompletionBlock(completion)
        self.pushViewController(viewController, animated: animated)
        CATransaction.commit()
    }
}

peut peut-être aider à comprendre si pushViewController se termine, si tous les viewControllers sont attendus.

Un autre test que j'essaie de faire consiste à lancer l'application avec iOS 8.x et iPhone 6+, car il y a quelques problèmes dans le projet pureLayout autour d'iOS 9. Pouvez-vous envoyer des commentaires sur ce test? 

J'ai quelques soupçons également sur la vraie dimension scrollview avant l'action pushview, pouvez-vous analyser la vue actuelle en examinant la hiérarchie des vues ?

2
Alessandro Ornano

Vérifiez si vous avez des codes inutiles comme ci-dessous dans votre BaseNavigationViewController: 

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRequireFailureOf otherGestureRecognizer: UIGestureRecognizer) -> Bool {
    return true
}

Ici, j'ai un exemple de code qui reproduit ce problème (blocage de l'application en poussant VC) que vous devez reproduire comme suit: 

  • Essayez d’utiliser le geste de pop contextuel (gauche | droite) lorsque aucun contrôleur de vue n’est situé en dessous (c.-à-d. Sur les contrôleurs de vue racine, votre contrôleur VC-Home).
  • Essayez ensuite de cliquer sur n'importe quel élément de l'interface utilisateur (ce qui vous poussera au prochain ViewController).

Exemple de code: https://github.com/aliuncoBamilo/TestNavigationPushBug

0
Aliunco

Essayez dans le fil principal

dispatch_async(dispatch_get_main_queue()){

UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("ViewController")
// your code

}
0
bourvill