ios - tutorial - uipageviewcontrollerdatasource
TransiciĆ³n de UIPageViewController ''Llamadas no balanceadas para comenzar/finalizar transiciones de apariencia para'' (10)
Aquí hay una versión rápida con el delegado:
agregue este código (asegúrese de incluir el UIPageViewControllerDelegate en su encabezado o extensión de clase y asigne self.pageViewController.delegate = self;
):
- (void)pageViewController:(UIPageViewController *)pageViewController willTransitionToViewControllers:(NSArray *)pendingViewControllers {
self.pageAnimationFinished = NO;
}
- (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed {
self.pageAnimationFinished = YES;
}
luego verifique self.pageAnimationFinished
y devuelva nil si es == NO
.
Explicación más larga:
- (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed
Podemos usar este método de delegado de UIPageViewControllerDelegate
para saber cuándo UIPageViewControllerDelegate
la animación de voltear o deslizar páginas. Usando esto solo podemos implementarlo así:
- (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed {
pageAnimationFinished = YES;
}
entonces, solo devuelve nil
en tu
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(PageViewController *)viewController
y
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(PageViewController *)viewController
cuando
pageAnimationFinished == NO
. Asegúrese de establecer pageAnimationFinished
en NO
cuando anima. La mejor manera de saber cuándo animas es usando el opuesto de
- (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed
a saber:
- (void)pageViewController:(UIPageViewController *)pageViewController willTransitionToViewControllers:(NSArray *)pendingViewControllers
No he visto esa advertencia desde entonces y esto se puede hacer en 1/3 de las líneas como las otras soluciones. Y es mucho más fácil de seguir.
Cuando UIPageViewController
por UIPageViewController
más rápido que su animación de transición, recibo '' Unbalanced calls to begin/end appearance transitions for <MyDataViewController>
'' y una de las dos vistas en horizontal no se muestra hasta que intento pasar la página.
¿Alguien tiene una idea para solucionar este error?
Aquí está la versión Swift de la respuesta de Bill Cheswick (actualmente la respuesta principal):
Agrega una variable para mantener el estado actual:
var pageIsAnimating = false
Establecer el estado de animación:
func pageViewController(pageViewController: UIPageViewController, willTransitionToViewControllers pendingViewControllers: [UIViewController]) {
self.pageIsAnimating = true
}
func pageViewController(pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
if finished || completed {
self.pageIsAnimating = false
}
}
Bloquea las transiciones si está animando actualmente:
func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
if self.pageIsAnimating {
return nil
}
// Your code here
}
func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
if self.pageIsAnimating {
return nil
}
// Your code here
}
¡Gracias Bill Cheswick!
Buena answer de Basem Saadawy pero tiene algún defecto.
En realidad, el gesto del delegado Reconocimiento de inicio de sesión: se puede llamar sin más animación iniciada. Esto es posible si comienza su gesto con el movimiento vertical del dedo y su desplazamiento horizontal no es suficiente para iniciar la animación (pero es suficiente para iniciar el gesto: Reconocimiento de inicio de página :) . Por lo tanto, nuestra variable pageAnimationFinished se establecerá en NO sin una animación real. Por lo tanto, nunca se llamará a pageViewController: didFinishAnimating: y se congela la página actual sin la posibilidad de cambiarla.
Es por eso que un mejor lugar para asignar NO a esta variable es un método de acción del reconocedor de gestos con un examen de su velocidad y traslación (solo nos interesa la dirección horizontal).
Así que los pasos finales son:
1) Declarar una variable de instancia (una bandera):
BOOL pageAnimationFinished;
2) Establecer su valor inicial.
- (void)viewDidLoad
{
[super viewDidLoad];
...
pageAnimationFinished = YES;
}
3) Asignar un delegado y una acción personalizada a los reconocedores de gestos de paneo
for (UIGestureRecognizer * gesRecog in self.pageViewController.gestureRecognizers)
{
if ([gesRecog isKindOfClass:[UIPanGestureRecognizer class]])
{
gesRecog.delegate = self;
[gr addTarget:self action:@selector(handlePan:)];
}
}
3 '') La animación comienza realmente cuando la traducción del gesto es mayor en dirección horizontal y el dedo se mueve horizontalmente en un momento.
Supongo que la misma lógica se usa en la acción del reconocedor interno asignada por UIPageViewController .
- (void) handlePan:(UIPanGestureRecognizer *)gestureRecognizer
{
if (pageAnimationFinished && gestureRecognizer.state == UIGestureRecognizerStateChanged)
{
CGPoint vel = [gestureRecognizer velocityInView:self.view];
CGPoint tr = [gestureRecognizer translationInView:self.view];
if (ABS(vel.x) > ABS(vel.y) && ABS(tr.x) > ABS(tr.y))
pageAnimationFinished = NO; // correct place
}
}
4) Deshabilitar un gesto si una animación no está terminada.
-(BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]] && ([gestureRecognizer.view isEqual:self.view] || [gestureRecognizer.view isEqual:self.pageViewController.view]))
{
UIPanGestureRecognizer * panGes = (UIPanGestureRecognizer *)gestureRecognizer;
if(!pageAnimationFinished || (currentPage < minimumPage && [panGes velocityInView:self.view].x < 0) || (currentPage > maximumPage && [panGes velocityInView:self.view].x > 0))
return NO;
}
return YES;
}
5) La animación está terminada.
- (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed
{
pageAnimationFinished = YES;
}
Jugué demasiado con él y parece que esta es una buena solución que funciona bien.
Intentaré ignorar el gesto en UIPageViewControllers durante la transición.
Las respuestas anteriores fueron correctas, pero creo que son más elaboradas de lo necesario, y el libro de cocina es útil. Así que aquí está lo que parece estar funcionando para mí:
En el controlador de vista que configura y llama a pageViewController, declare:
@property (assign) BOOL pageIsAnimating;
y en viewDidLoad:
pageIsAnimating = NO;
Agrega esto:
- (void)pageViewController:(UIPageViewController *)pageViewController willTransitionToViewControllers:(NSArray *)pendingViewControllers {
pageIsAnimating = YES;
}
y agrega un par de líneas a:
- (void)pageViewController:(UIPageViewController *)pageViewController
didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers
transitionCompleted:(BOOL)completed {
if (completed || finished) // Turn is either finished or aborted
pageIsAnimating = NO;
...
}
Los gestos se suprimen al negarse a proporcionar información del controlador de vista:
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController
viewControllerAfterViewController:(UIViewController *)viewController {
if (pageIsAnimating)
return nil;
...
return after;
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController
viewControllerBeforeViewController:(UIViewController *)viewController {
if (pageIsAnimating)
return nil;
...
return before;
}
Ah, y los cambios de orientación restablecen la bandera:
- (UIPageViewControllerSpineLocation)pageViewController:(UIPageViewController *)pageViewController
spineLocationForInterfaceOrientation:(UIInterfaceOrientation)orientation {
pageIsAnimating = NO;
...
}
Mi solución en rapidez, sencillez y trabajo:
- Establezca pageviewcontroller delegate en su clase
Agregue el siguiente código
extension MyPageVC: UIPageViewControllerDelegate { func pageViewController(_ pageViewController: UIPageViewController, willTransitionTo pendingViewControllers: [UIViewController]) { self.view.isUserInteractionEnabled = false } func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) { self.view.isUserInteractionEnabled = true } }
Qué tal esto:
- (void)pageViewController:(UIPageViewController*)pgVC willTransitionToViewControllers:(NSArray*)pendingVCs
{
pgVC.dataSource = nil; // ... to disallow user to change pages until animation completes
}
- (void)pageViewController:(UIPageViewController*)pgVC
didFinishAnimating:(BOOL)finished
previousViewControllers:(NSArray*)prevVCs
transitionCompleted:(BOOL)completed
{
if(completed || finished) {
pgVC.dataSource = _pgViewDataSource; // ... to allow user to change pages again
}
}
Resuelto siguiendo estos pasos:
1- Declare una bandera para indicar que la animación ha terminado o no:
BOOL pageAnimationFinished;
2- Establezca este indicador en verdadero en viewDidLoad:
pageAnimationFinished = YES;
3- Desactive tapGesture para el controlador de la páginaView y asigne ''self'' a panGestureRecognizer delegate:
for (UIGestureRecognizer * gesRecog in self.pageViewController.gestureRecognizers)
{
if ([gesRecog isKindOfClass:[UITapGestureRecognizer class]])
gesRecog.enabled = NO;
else if ([gesRecog isKindOfClass:[UIPanGestureRecognizer class]])
gesRecog.delegate = self;
}
4- Permitir / No permitir a panGestureRecognizer a través del siguiente método de delegado del reconocedor de gestos:
-(BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]] && ([gestureRecognizer.view isEqual:self.view] || [gestureRecognizer.view isEqual:self.pageViewController.view]))
{
UIPanGestureRecognizer * panGes = (UIPanGestureRecognizer *)gestureRecognizer;
if(!pageAnimationFinished || (currentPage < minimumPage && [panGes velocityInView:self.view].x < 0) || (currentPage > maximumPage && [panGes velocityInView:self.view].x > 0))
return NO;
pageAnimationFinished = NO;
}
return YES;
}
5- Agregue el siguiente método delegado de pageViewController:
- (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed
{
pageAnimationFinished = YES;
}
Tuve que agregarlo a viewDidAppear: para que funcione
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
self.pageAnimationFinished = YES;
}
Utilice sus métodos UIPageViewControllerDelegate y configure guardas para evitar la creación de nuevas vistas de página cuando se detectan giros de página excesivos.
- Puede desactivar los reconocedores de gestos
- Establecer "userInteraction" en deshabilitado en UIView
- mantener una bandera en el UIPageViewController para ignorar más entradas cuando se produce una animación. (advertencia sobre esta opción ... ios5 y ios6 tienen diferentes maneras de determinar cuándo comenzó la animación ...)