64 votes

UIPageViewController, comment puis-je accéder correctement à une page spécifique sans perturber l'ordre spécifié par la source de données?

J'ai trouvé quelques questions sur la façon de faire un UIPageViewController accéder à une page spécifique, mais j'ai remarqué un problème ajouté avec le saut qu'aucune des réponses semblent reconnaître.

Sans entrer dans les détails de mon application iOS (qui est semblable à un paginé calendrier), voici ce que je suis en train de vivre. Je déclare une UIPageViewController, - vue-contrôleur, et de mettre en œuvre une source de données.

// end of the init method
        pageViewController = [[UIPageViewController alloc] 
        initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll
          navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal
                        options:nil];
        pageViewController.dataSource = self;
        [self jumpToDay:0];
}

//...

- (void)jumpToDay:(NSInteger)day {
        UIViewController *controller = [self dequeuePreviousDayViewControllerWithDaysBack:day];
        [pageViewController setViewControllers:@[controller]
                                    direction:UIPageViewControllerNavigationDirectionForward
                                     animated:YES
                                   completion:nil];
}

- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController {
        NSInteger days = ((THDayViewController *)viewController).daysAgo;
        return [self dequeuePreviousDayViewControllerWithDaysBack:days + 1];
}

- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController {
        NSInteger days = ((THDayViewController *)viewController).daysAgo;
        return [self dequeuePreviousDayViewControllerWithDaysBack:days - 1];
}

- (UIViewController *)dequeuePreviousDayViewControllerWithDaysBack:(NSInteger)days {
        return [[THPreviousDayViewController alloc] initWithDaysAgo:days];
}

Modifier Note: j'ai ajouté simplifié le code pour la file d'attente de la méthode. Même avec cette mise en œuvre blasphématoire j'ai exactement le même problème avec l'ordre des pages.

L'initialisation de tous les travaux comme prévu. L'augmentation de la pagination tout fonctionne très bien. Le problème c'est que si j'appel "jumpToDay" encore une fois, l'ordre devient confuse.

Si l'utilisateur est sur la journée -5 et les sauts de la journée 1, un défilement vers la gauche pour révéler le jour de -5 à nouveau à la place des 0 jours. Cela semble avoir quelque chose à voir avec la façon dont UIPageViewController conserve une référence à la proximité de pages, mais je ne trouve pas de référence à une méthode qui permettrait de forcer l'actualisation du cache.

Des idées?

84voto

djibouti33 Points 3807

Programmation iOS6, par Matt Neuburg documents exacte de ce problème, et j'ai effectivement trouvé que cette solution se sent un peu mieux que la actuellement accepté de répondre. Cette solution, qui fonctionne très bien, a un effet secondaire négatif de l'animation à l'image avant/après, et puis discordante remplacement de cette page avec la page souhaitée. J'ai ressenti que c'était une drôle d'expérience utilisateur, et Matt solution prend soin de cela.

__weak UIPageViewController* pvcw = pvc;
[pvc setViewControllers:@[page]
              direction:UIPageViewControllerNavigationDirectionForward
               animated:YES completion:^(BOOL finished) {
                   UIPageViewController* pvcs = pvcw;
                   if (!pvcs) return;
                   dispatch_async(dispatch_get_main_queue(), ^{
                       [pvcs setViewControllers:@[page]
                                  direction:UIPageViewControllerNavigationDirectionForward
                                   animated:NO completion:nil];
                   });
               }];

37voto

Spencer Hall Points 894

Alors j'ai couru dans la même situation que vous l'endroit où je devais être capable de "sauter" d'une page, puis trouva la commande foiré quand j'ai fait un geste en arrière d'une page. Aussi loin que j'ai été en mesure de dire, l'affichage de la page de contrôleur est certainement la mise en cache à la vue des contrôleurs et quand vous "sauter" d'une page, vous devez spécifier la direction: en avant ou en arrière. Il suppose alors que le nouveau-vue-contrôleur est un "voisin" à la vue précédente contrôleur et donc automatiquement présente la vue précédente contrôleur lorsque vous geste en retour. J'ai trouvé que cela se produit uniquement lorsque vous utilisez la UIPageViewControllerTransitionStylescroll et pas UIPageViewControllerTransitionStylepagecurl. La page curl style apparemment ne fait pas de même de la mise en cache depuis si vous "sauter" d'une page puis le geste de retour, il offre la pageViewController:viewController(Avant/Après)ViewController: message à la source de données permettant de vous fournir le bon voisin-vue-contrôleur.

Solution: Lors de l'exécution d'un "saut" de la page, vous pouvez sauter vers le voisin de la page à la page (animation:NON) vous êtes le saut, puis à la fin du bloc de saut, saut à la page désirée. Cela permettra de mettre à jour le cache de telle manière que lorsque vous les gestes de retour, le bon voisin page sera affichée. L'inconvénient est que vous devrez créer deux contrôleurs de vue; celui que vous êtes en sautant et celui qui doit être affiché après des gestes.

Voici le code à une catégorie que j'ai écrit pour UIPageViewController:

@implementation UIPageViewController (Additions)

 - (void)setViewControllers:(NSArray *)viewControllers direction:(UIPageViewControllerNavigationDirection)direction invalidateCache:(BOOL)invalidateCache animated:(BOOL)animated completion:(void (^)(BOOL finished))completion {
    NSArray *vcs = viewControllers;
    __weak UIPageViewController *mySelf = self;

    if (invalidateCache && self.transitionStyle == UIPageViewControllerTransitionStyleScroll) {
        UIViewController *neighborViewController = (direction == UIPageViewControllerNavigationDirectionForward
                                                    ? [self.dataSource pageViewController:self viewControllerBeforeViewController:viewControllers[0]]
                                                    : [self.dataSource pageViewController:self viewControllerAfterViewController:viewControllers[0]]);
        [self setViewControllers:@[neighborViewController] direction:direction animated:NO completion:^(BOOL finished) {
            [mySelf setViewControllers:vcs direction:direction animated:animated completion:completion];
        }];
    }
    else {
        [mySelf setViewControllers:vcs direction:direction animated:animated completion:completion];
    }
}

@end

Ce que vous pouvez faire pour tester cela est de créer une nouvelle Page Basée sur l'Application "et d'ajouter" un "goto" bouton "saut" d'un certain mois et ensuite geste en retour. Assurez-vous de définir le style de transition pour les faire défiler.

5voto

jcesarmobile Points 6086

J'utilise cette fonction (je suis toujours en mode paysage, mode 2 pages)

 -(void) flipToPage:(NSString * )index {


int x = [index intValue];
LeafletPageContentViewController *theCurrentViewController = [self.pageViewController.viewControllers   objectAtIndex:0];

NSUInteger retreivedIndex = [self indexOfViewController:theCurrentViewController];

LeafletPageContentViewController *firstViewController = [self viewControllerAtIndex:x];
LeafletPageContentViewController *secondViewController = [self viewControllerAtIndex:x+1 ];


NSArray *viewControllers = nil;

viewControllers = [NSArray arrayWithObjects:firstViewController, secondViewController, nil];


if (retreivedIndex < x){

    [self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:NULL];

} else {

    if (retreivedIndex > x ){

        [self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionReverse animated:YES completion:NULL];
      } 
    }
} 
 

2voto

Peter Boné Points 91

Je peux confirmer ce problème et que cela ne se produit que lorsque vous utilisez UIPageViewControllerTransitionStyleScroll et non UIPageViewControllerTransitionStylePageCurl .

Solution: faites une boucle et appelez UIPageViewController setViewControllers pour chaque tour de page, jusqu'à atteindre la page souhaitée.

Cela permet de synchroniser l'index de source de données interne dans UIPageViewController.

1voto

yar1vn Points 1472

J'ai eu une approche différente, devrait être possible si vos pages sont conçues pour être mises à jour après init:
Quand une page de manuel est sélectionnée, je mets à jour un drapeau

 - (void)scrollToPage:(NSInteger)page animated:(BOOL)animated
{
    if (page != self.currentPage) {
        [self setViewControllers:@[[self viewControllerForPage:page]]
                       direction:(page > self.currentPage ?
                                  UIPageViewControllerNavigationDirectionForward :
                                  UIPageViewControllerNavigationDirectionReverse)
                        animated:animated
                      completion:nil];
        self.currentPage = page;
        self.forceReloadNextPage = YES; // to override view controller automatic page cache
    }
}

- (ScheduleViewController *)viewControllerForPage:(NSInteger)page
{
    CustomViewController * scheduleViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"CustomViewController"];
    scheduleViewController.view.tag = page; // keep track of pages using view.tag property
    scheduleViewController.data = [self dataForPage:page];

    if (self.currentViewController)
        scheduleViewController.calendarLayoutHourHeight = self.currentViewController.calendarLayoutHourHeight;

    return scheduleViewController;
}
 

puis forcez la page suivante à recharger avec les données correctes:

 - (void)pageViewController:(UIPageViewController *)pageViewController willTransitionToViewControllers:(NSArray *)pendingViewControllers
{
    CustomViewController * nextViewController = [pendingViewControllers lastObject];

    // When manual scrolling occurs, the next page is loaded from UIPageViewController cache
    //  and must be refreshed
    if (self.forceReloadNextPage) {
        // calculate the direction of the scroll to know if to load next or previous page
        NSUInteger page = self.currentPage + 1;
        if (self.currentPage > nextViewController.view.tag) page = self.currentPage - 1;

        nextViewController.data = [self dataForPage:page];
        self.forceReloadNextPage = NO;
    }
}
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X