Il est étrange qu'il n'y ait pas de moyen simple de faire cela. Considérez le scénario suivant:
J'ai essayé de supprimer le contrôleur de vue en tant que contrôleur de vue enfant une fois la transition terminée, mais cela me permet toujours de revenir à la page vide (cela ne "redimensionne" pas la vue de page)
Est-ce que ce que je veux faire est possible?
En conclusion de la bonne réponse de Matt Mc, la méthode suivante pourrait être ajoutée à une sous-classe de UIPageViewController, autorisant ainsi l'utilisation de setViewControllers: direction: animée: complétion: comme elle était destinée à être utilisée si le bogue n'était pas présent.
- (void) setViewControllers:(NSArray*)viewControllers direction:(UIPageViewControllerNavigationDirection)direction animated:(BOOL)animated completion:(void (^)(BOOL))completion {
if (!animated) {
[super setViewControllers:viewControllers direction:direction animated:NO completion:completion];
return;
}
[super setViewControllers:viewControllers direction:direction animated:YES completion:^(BOOL finished){
if (finished) {
dispatch_async(dispatch_get_main_queue(), ^{
[super setViewControllers:viewControllers direction:direction animated:NO completion:completion];
});
} else {
if (completion != NULL) {
completion(finished);
}
}
}];
}
Maintenant, appelez simplement setViewControllers: direction: animated: complétion: sur la classe/les sous-classes implémentant cette méthode, et cela devrait fonctionner comme prévu.
maq a raison. Si vous utilisez la transition de défilement, la suppression d'un contrôleur de vue enfant de UIPageViewController n'empêche pas la "page" supprimée de revenir à l'écran si l'utilisateur y accède. Si cela vous intéresse, voici comment j'ai supprimé le contrôleur de vue enfant de UIPageViewController.
// deleteVC is a child view controller of the UIPageViewController
[deleteVC willMoveToParentViewController:nil];
[deleteVC.view removeFromSuperview];
[deleteVC removeFromParentViewController];
Le contrôleur de vue deleteVC est supprimé de la propriété childViewControllers de UIPageViewController, mais reste affiché à l'écran si l'utilisateur y accède.
En attendant qu'une personne plus intelligente que moi trouve une solution élégante, voici une solution (c'est un hack - vous devez donc vous demander si vous avez vraiment besoin de supprimer des pages d'un UIPageViewController).
Ces instructions supposent qu'une seule page est affichée à la fois.
Une fois que l'utilisateur a appuyé sur un bouton lui indiquant qu'il souhaite supprimer la page, accédez à la page suivante ou précédente à l'aide de la méthode setViewControllers: direction: animated: completion:. Bien entendu, vous devez ensuite supprimer le contenu de la page de votre modèle de données.
Ensuite (et voici le hack), créez et configurez un tout nouveau UIPageViewController et chargez-le au premier plan (c'est-à-dire devant l'autre UIPageViewController). Assurez-vous que le nouveau UIPageViewController commence par afficher exactement la même page que celle précédemment affichée. Votre nouveau UIPageViewController va récupérer les nouveaux contrôleurs de vue de la source de données.
Enfin, déchargez et détruisez le UIPageViewController qui se trouve en arrière-plan.
Quoi qu'il en soit, maq a posé une très bonne question. Malheureusement, je n'ai pas assez de points de réputation pour pouvoir voter. Ah, osez rêver ... un jour, j'aurai 15 points de réputation.
Je vais mettre cette réponse ici juste pour ma propre référence future et si cela peut aider quelqu'un - ce que j'ai fini par faire était:
setViewControllers
, j'ai créé/initié un nouveau UIPageViewController avec les données modifiées (élément supprimé) et je l'ai poussé sans animer, donc rien ne change à l'écran (mon UIPageViewController est contenu dans un UINavigationController)viewControllers
du UINavigationController, supprimez l'avant-dernier contrôleur de vue (l'ancien UIPageViewController).Pour améliorer cela. Vous devez détecter si pageView défile ou non avant setViewControllers.
var isScrolling = false
func viewDidLoad() {
...
for v in view.subviews{
if v.isKindOfClass(UIScrollView) {
(v as! UIScrollView).delegate = self
}
}
}
func scrollViewWillBeginDragging(scrollView: UIScrollView){
isScrolling = true
}
func scrollViewDidEndDecelerating(scrollView: UIScrollView){
isScrolling = false
}
func jumpToVC{
if isScrolling { //you should not jump out when scrolling
return
}
setViewControllers([vc], direction:direction, animated:true, completion:{[unowned self] (succ) -> Void in
if succ {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.setViewControllers([vc], direction:direction, animated:false, completion:nil)
})
}
})
}
Ce problème existe lorsque vous essayez de modifier viewControllers lors d'une animation de geste de balayage entre viewControllers. Pour résoudre ce problème, j'ai créé un simple drapeau à découvrir lorsque le contrôleur de vue de page est en cours de transition.
- (void)pageViewController:(UIPageViewController *)pageViewController willTransitionToViewControllers:(NSArray *)pendingViewControllers {
self.pageViewControllerTransitionInProgress = YES;
}
- (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed {
self.pageViewControllerTransitionInProgress = NO;
}
Et quand j'essaye de changer la vue, je vérifie s'il y a une transition en cours.
- (void)setCurrentPage:(NSString *)newCurrentPageId animated:(BOOL)animated {
if (self.pageViewControllerTransitionInProgress) {
return;
}
[self.pageContentViewController setViewControllers:@[pageDetails] direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:^(BOOL finished) {
}
}
Je viens d'apprendre cela moi-même, alors prenez avec un grain de sel, mais de ce que je comprends, vous devez changer la source de données du contrôleur de pageview, pas supprimer le contrôleur de vue. Le nombre de pages affichées dans un contrôleur pageview est déterminé par sa source de données, et non par les contrôleurs de vue.
NSMutableArray *mArray = [[NSMutableArray alloc] initWithArray:self.userArray];
[mArray removeObject:userToBlock];
self.userArray = mArray;
UIViewController *startingViewController = [self viewControllerAtIndex:atIndex-1];
NSArray *viewControllers = @[startingViewController];
[self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionReverse animated:NO completion:nil];
Je n'ai pas vraiment eu le temps de lire les commentaires ci-dessus, mais cela a fonctionné pour moi. Fondamentalement, je supprime les données (dans mon cas, un utilisateur), puis je passe à la page précédente. Fonctionne comme un charme. J'espère que cela aidera ceux qui recherchent une solution rapide.
Dans une situation similaire, je souhaitais que l'utilisateur puisse "appuyer et supprimer" n'importe quelle page de UIPageViewController. Après avoir joué un peu avec, j'ai trouvé une solution plus simple que celles décrites ci-dessus:
Faites un saut temporaire vers un endroit "sûr" (idéalement le dernier). Utilisez animation: NO pour que cela se produise instantanément.
UIViewController *jumpToAnotherViewController = [self viewControllerAtIndex:dyingPageIndex-1];
[self setViewControllers:@[jumpToAnotherViewController] direction:UIPageViewControllerNavigationDirectionReverse animated:NO completion:nil];
Supprimer la page sélectionnée du modèle/source de données.
Maintenant, si c'est la dernière page:
Allez-y, cette fois avec animée: OUI.
jumpToAnotherViewController = [self viewControllerAtIndex:dyingPageIndex-1];
[self setViewControllers:@[jumpToAnotherViewController] direction:UIPageViewControllerNavigationDirectionReverse animated:YES completion:nil];
Dans le cas où ce n'était PAS la dernière page:
Allez-y, cette fois avec animée: OUI.
jumpToAnotherViewController = [self viewControllerAtIndex:dyingPageIndex;
[self setViewControllers:@[jumpToAnotherViewController] direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:nil];
C'est tout! Cela fonctionne bien dans XCode 6.1.1.
Code complet ci-dessous. Ce code est dans mon UIPageViewController et est appelé par un délégué de la page à supprimer. Dans mon cas, la suppression de la première page n'était pas autorisée car elle contenait des éléments différents du reste des pages. Bien sûr, vous devez substituer:
[YourModel deletePage:] avec le code pour supprimer la page mourante de votre modèle
- (void)deleteAViewController:(id)sender {
YourUIViewController *dyingGroup = (YourUIViewController *)sender;
NSUInteger dyingPageIndex = dyingGroup.pageIndex;
// Check to see if we are in the last page as it's a special case.
BOOL isTheLastPage = (dyingPageIndex >= YourTotalNumberOfPagesFromModel.count);
// Make a temporary jump back - make sure to use animated:NO to have it jump instantly
UIViewController *jumpToAnotherViewController = [self viewControllerAtIndex:dyingPageIndex-1];
[self setViewControllers:@[jumpToAnotherViewController] direction:UIPageViewControllerNavigationDirectionReverse animated:NO completion:nil];
// Now delete the selected group from the model, setting the target
[YourModel deletePage:dyingPageIndex];
if (isTheLastPage) {
// Now jump to the definitive controller. In this case, it's the same one, we're just reloading it to refresh the data source.
// This time we're using animated:YES
jumpToAnotherViewController = [self viewControllerAtIndex:dyingPageIndex-1];
[self setViewControllers:@[jumpToAnotherViewController] direction:UIPageViewControllerNavigationDirectionReverse animated:YES completion:nil];
} else {
// Now jump to the definitive controller. This reloads the data source. This time we're using animated:YES
jumpToAnotherViewController = [self viewControllerAtIndex:dyingPageIndex];
[self setViewControllers:@[jumpToAnotherViewController] direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:nil];
}
}
En fait, je pense avoir résolu le problème. Voici ce qui se passait dans mon application:
Il l'a fait ce qui suit dans Swift 3.0
fileprivate var isAnimated:Bool = false
override func setViewControllers(_ viewControllers: [UIViewController]?, direction: UIPageViewControllerNavigationDirection, animated: Bool, completion: ((Bool) -> Void)? = nil) {
if self.isAnimated {
delay(0.5, closure: {
self.setViewControllers(viewControllers, direction: direction, animated: animated, completion: completion)
})
}else {
super.setViewControllers(viewControllers, direction: direction, animated: animated, completion: completion)
}
}
extension SliderViewController:UIPageViewControllerDelegate {
func pageViewController(_ pageViewController: UIPageViewController, willTransitionTo pendingViewControllers: [UIViewController]) {
self.isAnimated = true
}
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
self.isAnimated = finished
}
}
Et voici la fonction delay
func delay(_ delay:Double, closure:@escaping ()->()) {
DispatchQueue.main.asyncAfter(
deadline: DispatchTime.now() + Double(Int64(delay * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC), execute: closure)
}