Mon UIPageViewController fonctionnait bien sous iOS 5. Mais lorsque iOS 6 est arrivé, je voulais utiliser le nouveau style de transition de défilement (UIPageViewControllerTransitionStyleScroll) au lieu du style de courbure de page. Cela a provoqué la rupture de mon UIPageViewController.
Cela fonctionne très bien sauf après que j'ai appelé setViewControllers:direction:animated:completion:
. Après cela, la prochaine fois que l'utilisateur fait défiler manuellement une page, nous obtenons la mauvaise page. Quel est le problème ici?
Ma solution de contournement de ce bogue consistait à créer un bloc une fois terminé, qui définissait le même contrôleur de vue, mais sans animation.
__weak YourSelfClass *blocksafeSelf = self;
[self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:^(BOOL finished){
if(finished)
{
dispatch_async(dispatch_get_main_queue(), ^{
[blocksafeSelf.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:NULL];// bug fix for uipageview controller
});
}
}];
C'est en fait un bogue dans UIPageViewController. Cela se produit uniquement avec le style de défilement (UIPageViewControllerTransitionStyleScroll) et uniquement après avoir appelé setViewControllers:direction:animated:completion:
avec une animation: YES. Il existe donc deux solutions de contournement:
N'utilisez pas UIPageViewControllerTransitionStyleScroll.
Ou, si vous appelez setViewControllers:direction:animated:completion:
, utilisez uniquement animated:NO
.
Pour voir le bogue clairement, appelez setViewControllers:direction:animated:completion:
puis, dans l'interface (en tant qu'utilisateur), naviguez manuellement (de retour) à la page précédente. Vous retournerez à la mauvaise page: pas la page précédente du tout, mais la page sur laquelle vous étiez lorsque setViewControllers:direction:animated:completion:
a été appelé.
Le bogue semble être dû au fait que, lorsque vous utilisez le style de défilement, UIPageViewController effectue une sorte de mise en cache interne. Ainsi, après l'appel à setViewControllers:direction:animated:completion:
, il ne parvient pas à effacer son cache interne. Il pense connaître la page précédente. Ainsi, lorsque l'utilisateur navigue à gauche vers la page précédente, UIPageViewController ne parvient pas à appeler la méthode dataSource pageViewController:viewControllerBeforeViewController:
, ou l'appelle avec le mauvais contrôleur de vue en cours.
J'ai posté un film qui montre clairement comment voir le bogue:
http://www.apeth.com/PageViewControllerBug.mov
EDIT Ce bogue sera probablement corrigé sous iOS 8.
EDIT Pour une autre solution intéressante à ce problème, consultez cette réponse: https://stackoverflow.com/a/21624169/341994
Ici est un "brut" Gist je mis ensemble. Il contient une alternative UIPageViewController qui souffre de la maladie d'Alzheimer (c'est-à-dire qu'il ne dispose pas de la mise en cache interne de l'implémentation Apple).
Ce cours n'est pas complet mais il fonctionne dans ma situation (à savoir: défilement horizontal).
Depuis iOS 12, le problème décrit dans la question initiale semble être presque résolu. Je suis venu à cette question parce que je l'ai expérimenté dans ma configuration particulière, dans laquelle cela se produit encore, d'où le mot "presque" ici.
La configuration dans laquelle j'ai rencontré ce problème était la suivante: 1) l'application a été ouverte via un lien profond 2) en fonction du lien, l'application devait basculer vers un onglet particulier et y ouvrir un élément donné via Push 3) le problème décrit n’est apparu que lorsque l’onglet cible n’était pas précédemment sélectionné par l’utilisateur (de sorte que UIPageViewController était supposé s’animer sur cet onglet) et uniquement lorsque setViewControllers:direction:animated:completion:
avait animated = true
UIPageViewController, ce dernier s’est avéré être un gros désordre - il présentait des contrôleurs de vue complètement erronés, même si le débogage montrait que tout allait bien au niveau logique
J'ai supposé que la racine du problème était que je poussais très rapidement le contrôleur de vue après l'appel de setViewControllers:direction:animated:completion:
, de sorte que UIPageViewController n'avait aucune chance de finir quelque chose (peut-être une animation, une mise en cache ou autre chose).
Donnez simplement à UIPageViewController un peu de temps libre en retardant ma navigation programmatique dans l'interface utilisateur via
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) { ... }
résolu le problème pour moi. Et cela a également rendu l’ouverture programmatique de l’élément lié plus conviviale visuellement.
J'espère que cela aide quelqu'un dans une situation similaire.
Ce bogue existe toujours dans iOS9. J'utilise la même solution de contournement que celle de George Tsifrikas, mais une version de Swift:
pageViewController.setViewControllers([page], direction: direction, animated: true) { done in
if done {
dispatch_async(dispatch_get_main_queue()) {
self.pageViewController.setViewControllers([page], direction: direction, animated: false, completion: {done in })
}
}
}