viewpager tutorial pageview page example control ios iphone objective-c uipageviewcontroller

ios - tutorial - uipageviewcontroller swift 4



Deshabilitar el rebote de UIPageViewController (7)

Deshabilitar el rebote de UIPageViewController

  1. Agregue el delegado <UIScrollViewDelegate> al encabezado de su UIPageViewController

  2. Establezca los delegados de UIScrollView subyacentes de UIPageViewController en su padre en viewDidLoad :

    for (UIView *view in self.view.subviews) { if ([view isKindOfClass:[UIScrollView class]]) { ((UIScrollView *)view).delegate = self; break; } }

  3. La implementación de scrollViewDidScroll es restablecer contentOffset al origen ( NOT (0,0), pero (bound.size.width, 0) ) cuando el usuario está llegando fuera de los límites, como este:

    - (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. Finalmente, la implementación de scrollViewWillEndDragging es para hacer frente a un escenario de error cuando el usuario pasa rápidamente de izquierda a derecha en la primera página, la primera página no rebota a la izquierda (debido a la función anterior), pero rebotará en el derecho causado por la (tal vez) velocidad del deslizamiento. Y finalmente, cuando se recupera, el UIPageViewController desencadenará un salto de página a la segunda página (que, por supuesto, no se espera).

    - (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

Código para poner en viewDidLoad :

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

Implementación para 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); } }

Implementación para 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); } }

Busqué mucho para este, pero no pude encontrar una solución adecuada todavía.

¿Es posible desactivar el efecto de rebote de un UIPageViewController y seguir usando el UIPageViewControllerTransitionStyleScroll ?


No estaba seguro de cómo administrar correctamente el currentIndex pero terminé haciendo

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 } } }


Otra opción es establecer ScrollView.bounce = false. Solucionó mi problema con el rebote de desplazamiento de pageViewController (por supuesto, no sobre ScrollView). Bounce está deshabilitado, y todas las páginas pueden desplazarse sin rebotes.


Si intenta desactivar el rebote para UIPageViewController.scrollView , definitivamente obtendrá una página quebrada pageViewController : el deslizamiento no funcionará. Entonces, no hagas eso:

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

Utilice la solución con la búsqueda de la referencia de UIPageViewController en las UIPageViewController UIPageViewController solo para deshabilitar el desplazamiento por completo:

@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

Solución para deshabilitar el rebote en UIPageViewController:

  1. Cree la categoría UIScrollView (por ej. CustomScrolling). UIScrollView es delegado de su reconocedor de gestos.
  2. Tenga en cuenta que su UIViewController (también baseVC como baseVC con UIPageViewController dentro) se comparte a través de AppDelegate . De lo contrario, puede usar tiempo de ejecución ( #import <objc/runtime.h> ) y agregar propiedad de referencia (a su controlador baseVC ) a la categoría.
  3. Categoría de implementación:

    @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. Importar el archivo de categoría #import "UIScrollView+CustomScrolling.h" en su baseVC , que usa UIPageViewController.


UIPageViewController en realidad no hace mucho por ti. Puede usar un UIScrollView con controladores de vista con bastante facilidad y deshabilitar el rebote en eso.

Solo haz algo como

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); }


Deshabilitar el rebote de UIPageViewController

Swift 2.2

Adición a las respuestas

1) Agregue UIScrollViewDelegate a UIPageViewController

extension PageViewController: UIScrollViewDelegate

2) Agregar a viewDidLoad

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

3) Agregue los métodos 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) } }


Editar: No use esta solución. Luego supe que esto introduce un error en el que aproximadamente el 5% de las veces, el usuario no puede navegar en la misma dirección. Tienen que regresar, y luego volver a avanzar para continuar.

Si está utilizando un UIPageViewControllerDataSource , una solución relativamente simple (y un poco hacky) es deshabilitar el rebote cada vez que se llama al método pageViewController:viewControllerBeforeViewController: delegate. Aquí hay una implementación de ejemplo:

@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