ios - tutorial - uipageviewcontroller swift 4
Deshabilitar el rebote de UIPageViewController (7)
Deshabilitar el rebote de UIPageViewController
Agregue el delegado
<UIScrollViewDelegate>
al encabezado de su UIPageViewControllerEstablezca 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; } }
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); } }
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:
- Cree la categoría
UIScrollView
(por ej. CustomScrolling).UIScrollView
es delegado de su reconocedor de gestos. - Tenga en cuenta que su
UIViewController
(tambiénbaseVC
comobaseVC
conUIPageViewController
dentro) se comparte a través deAppDelegate
. De lo contrario, puede usar tiempo de ejecución (#import <objc/runtime.h>
) y agregar propiedad de referencia (a su controladorbaseVC
) a la categoría. 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
Importar el archivo de categoría
#import "UIScrollView+CustomScrolling.h"
en subaseVC
, 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