web-dev-qa-db-fra.com

Désactiver le rebond UIPageViewController

Beaucoup recherché pour celui-ci, mais n'a pas pu trouver une solution appropriée pour le moment. 

Est-il possible de désactiver l'effet de rebond d'une UIPageViewController tout en utilisant la UIPageViewControllerTransitionStyleScroll

41
Mario

Désactiver le rebond de UIPageViewController

  1. Ajoutez le délégué <UIScrollViewDelegate> à l'en-tête de votre UIPageViewController

  2. Définissez les délégués UIScrollView sous-jacents de UIPageViewController sur leur parent dans viewDidLoad:

    for (UIView *view in self.view.subviews) {
        if ([view isKindOfClass:[UIScrollView class]]) {
            ((UIScrollView *)view).delegate = self;
            break;
        }
    }
    
  3. L’implémentation de scrollViewDidScroll consiste à réinitialiser le contentOffset à l’origine ( NOT (0,0), mais (bound.size.width, 0) ) lorsque l’utilisateur dépasse les limites, comme ce:

    - (void)scrollViewDidScroll:(UIScrollView *)scrollView {
        if (_currentPage == 0 && scrollView.contentOffset.x < scrollView.bounds.size.width) {
            scrollView.contentOffset = CGPointMake(scrollView.bounds.size.width, 0);
        } else if (_currentPage == totalViewControllersInPageController-1 && scrollView.contentOffset.x > scrollView.bounds.size.width) {
            scrollView.contentOffset = CGPointMake(scrollView.bounds.size.width, 0);
        }
    }
    
  4. Enfin, l’implémentation de scrollViewWillEndDragging consiste à gérer un scénario de bogue lorsque l’utilisateur balaie rapidement de gauche à droite sur la première page, la première page ne rebondissant pas à gauche (en raison de la fonction ci-dessus), va rebondir à la droite causée par la vitesse (peut-être) du glissement. Et enfin, une fois renvoyé, UIPageViewController déclenchera un basculement de page vers la 2e page (ce qui n'est bien sûr pas prévu).

    - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
        if (_currentPage == 0 && scrollView.contentOffset.x <= scrollView.bounds.size.width) {
            *targetContentOffset = CGPointMake(scrollView.bounds.size.width, 0);
        } else if (_currentPage == totalViewControllersInPageController-1 && scrollView.contentOffset.x >= scrollView.bounds.size.width) {
            *targetContentOffset = CGPointMake(scrollView.bounds.size.width, 0);
        }
    }
    

Swift 4.0

Code à mettre dans viewDidLoad:

for subview in self.view.subviews {
    if let scrollView = subview as? UIScrollView {
        scrollView.delegate = self
        break;
    }
}

Mise en oeuvre pour scrollViewDidScroll :

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    if (currentPage == 0 && scrollView.contentOffset.x < scrollView.bounds.size.width) {
        scrollView.contentOffset = CGPoint(x: scrollView.bounds.size.width, y: 0);
    } else if (currentPage == totalViewControllersInPageController - 1 && scrollView.contentOffset.x > scrollView.bounds.size.width) {
        scrollView.contentOffset = CGPoint(x: scrollView.bounds.size.width, y: 0);
    }
}

Mise en oeuvre pour scrollViewWillEndDragging :

func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
    if (currentPage == 0 && scrollView.contentOffset.x <= scrollView.bounds.size.width) {
        targetContentOffset.pointee = CGPoint(x: scrollView.bounds.size.width, y: 0);
    } else if (currentPage == totalViewControllersInPageController - 1 && scrollView.contentOffset.x >= scrollView.bounds.size.width) {
        targetContentOffset.pointee = CGPoint(x: scrollView.bounds.size.width, y: 0);
    }
}
63
Dong Ma

Désactiver le rebond de UIPageViewController

Swift 2.2

Ajout aux réponses

1) Ajoutez UIScrollViewDelegate à UIPageViewController

extension PageViewController: UIScrollViewDelegate

2) Ajouter à viewDidLoad

for view in self.view.subviews {
   if let scrollView = view as? UIScrollView {
      scrollView.delegate = self
   }
}

3) Ajouter des méthodes UIScrollViewDelegate

func scrollViewDidScroll(scrollView: UIScrollView) {
    if currentIndex == 0 && scrollView.contentOffset.x < scrollView.bounds.size.width {
        scrollView.contentOffset = CGPoint(x: scrollView.bounds.size.width, y: 0)
    } else if currentIndex == totalViewControllers - 1 && scrollView.contentOffset.x > scrollView.bounds.size.width {
        scrollView.contentOffset = CGPoint(x: scrollView.bounds.size.width, y: 0)
    }
}

func scrollViewWillEndDragging(scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
    if currentIndex == 0 && scrollView.contentOffset.x < scrollView.bounds.size.width {
        scrollView.contentOffset = CGPoint(x: scrollView.bounds.size.width, y: 0)
    } else if currentIndex == totalViewControllers - 1 && scrollView.contentOffset.x > scrollView.bounds.size.width {
        scrollView.contentOffset = CGPoint(x: scrollView.bounds.size.width, y: 0)
    }
}
13
ZAV

Je ne savais pas comment gérer correctement la currentIndex mais finis par le faire 

extension Main: UIPageViewControllerDelegate {
    func pageViewController(pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
        if completed {
            guard let viewController = pageViewController.viewControllers?.first,
                index = viewControllerDatasource.indexOf(viewController) else {
                fatalError("Can't prevent bounce if there's not an index")
            }
            currentIndex = index
        }
    }
}
3
mwright

UIPageViewController ne fait pas grand chose pour vous. Vous pouvez utiliser un UIScrollView avec des contrôleurs de vue assez facilement et désactiver le rebond sur celui-ci.

Juste faire quelque chose comme

int x=0;
for (NSString *storyboardID in storyboardIDs){
        UIViewController *vc = [storyboard instantiateViewControllerWithIdentifier:storyboardID];
        [self addChildViewController:vc];
        vc.view.frame = CGRectMake(x++*vc.view.frame.size.width, 0, vc.view.frame.size.width, vc.view.frame.size.height);
        [self.scrollView addSubview:vc.view];
        [vc didMoveToParentViewController:self];
        self.scrollView.contentSize = CGSizeMake(storyboardIDs.count*vc.view.frame.size.width, vc.view.frame.size.height);
}
1
arsenius

Une autre option consiste à définir ScrollView.bounce = false . Il a résolu mon problème avec le défilement défilant de pageViewController (bien sûr, à propos de ScrollView) . Le rebond est désactivé et toutes les pages peuvent défiler sans rebondissements.

0
Baron Michael B

L'approche de @Dong Ma est parfaite, mais elle peut être un peu améliorée et simplifiée.

Code à mettre dans viewDidLoad :

for subview in view.subviews {
    if let scrollView = subview as? UIScrollView {
        scrollView.delegate = self
        break
    }
}

Mise en oeuvre pour scrollViewDidScroll :

public func scrollViewDidScroll(_ scrollView: UIScrollView) {
    if (currentPage == 0 && scrollView.contentOffset.x < scrollView.bounds.size.width) || (currentPage == totalNumberOfPages - 1 && scrollView.contentOffset.x > scrollView.bounds.size.width) {
      scrollView.contentOffset = CGPoint(x: scrollView.bounds.size.width, y: 0)
    }
  }

Mise en oeuvre pour scrollViewWillEndDragging :

public func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
    if (currentPage == 0 && scrollView.contentOffset.x <= scrollView.bounds.size.width) || (currentPage == totalNumberOfPages - 1 && scrollView.contentOffset.x >= scrollView.bounds.size.width) {
      targetContentOffset.pointee = CGPoint(x: scrollView.bounds.size.width, y: 0)
    }
  }
0
KrLx_roller

Edit: Ne pas utiliser cette solution. J'ai appris par la suite que cela introduisait un bogue dans lequel environ 5% du temps, l'utilisateur ne pouvait pas paginer dans la même direction. Ils doivent faire une page en arrière, puis avancer à nouveau pour continuer. 

Si vous utilisez une UIPageViewControllerDataSource, une solution de contournement relativement simple (et un peu compliquée) consiste à désactiver le rebondissement à chaque appel de la méthode de délégation pageViewController:viewControllerBeforeViewController:. Voici un exemple d'implémentation:

@interface YourDataSourceObject ()
@property (strong, nonatomic) UIScrollView *scrollView;
@end

@implementation
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController {
    if (!self.scrollView) {
        for (UIView *view in pageViewController.view.subviews) {
            if ([view isKindOfClass:[UIScrollView class]]) {
                self.scrollView = (UIScrollView *)view;
            }
        }
    }
    self.scrollView.bounces = NO;

    // Your other logic to return the correct view controller. 
}
@end
0
guptron

Si vous essayez de désactiver le rebond pour UIPageViewController.scrollView, vous obtiendrez certainement une pageViewController cassée: le balayage ne fonctionnera pas. Alors, ne fais pas ça:

self.theScrollView.alwaysBounceHorizontal = NO;
self.theScrollView.bounces = NO;

Utilisez la solution avec la référence scrollView dans les sous-vues UIPageViewController uniquement pour désactiver complètement le défilement:

@interface MyPageViewController : UIPageViewController
@property (nonatomic, assign) BOOL scrollEnabled;
@end

@interface MyPageViewController ()
@property (nonatomic, weak) UIScrollView *theScrollView;
@end

@implementation MyPageViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    for (UIView *view in self.view.subviews) {
        if ([view isKindOfClass:UIScrollView.class]) {
            self.theScrollView = (UIScrollView *)view;
            break;
        }
    }
}

- (void)setScrollEnabled:(BOOL)scrollEnabled
{
    _scrollEnabled = scrollEnabled;
    self.theScrollView.scrollEnabled = scrollEnabled;
}

@end

Solution pour désactiver le rebond sur UIPageViewController:

  1. Créez la catégorie UIScrollView (par exemple, CustomScrolling). UIScrollView est déjà délégué de son identificateur de geste.
  2. Sachez que votre cible UIViewController (ou baseVC avec UIPageViewController à l'intérieur) est partagée via AppDelegate. Sinon, vous pouvez utiliser le moment d'exécution (#import <objc/runtime.h>) et ajouter une propriété de référence (à votre contrôleur baseVC) à la catégorie.
  3. Mettre en œuvre la catégorie:

    @interface UIScrollView (CustomScrolling) <UIGestureRecognizerDelegate>
    @end
    
    @implementation UIScrollView (CustomScrolling)
    
    - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
    {
        UIViewController * baseVC = [(AppDelegate *)[[UIApplication sharedApplication] delegate] baseVC];
        if (gestureRecognizer.view == baseVC.pageViewController.theScrollView) {
            NSInteger page = [baseVC selectedIndex];
            NSInteger total = [baseVC viewControllers].count;
            UIPanGestureRecognizer *recognizer = (UIPanGestureRecognizer *)gestureRecognizer;
            CGPoint velocity = [recognizer velocityInView:self];
            BOOL horizontalSwipe = fabs(velocity.x) > fabs(velocity.y);
            if (!horizontalSwipe) {
                return YES;
            }
            BOOL scrollingFromLeftToRight = velocity.x > 0;
            if ((scrollingFromLeftToRight && page > 0) || (!scrollingFromLeftToRight && page < (total - 1))) {
                return YES;
            }
            return NO;
        }
        return YES;
    }
    
    @end
    
  4. Importez le fichier de catégorie #import "UIScrollView+CustomScrolling.h" dans votre baseVC, qui utilise UIPageViewController.

0
sig