ios - uipangesturerecognizer swift 4
Reconocedores de gestos UIPageViewController (14)
He estado trabajando en una aplicación por un tiempo, pero no pude hacer esta pregunta debido a la NDA.
Tengo una carga UIPageViewController con mi Viewcontroller. Los controladores de vista tienen botones que son anulados por los reconocedores de gestos de PageViewControllers. Por ejemplo, tengo un botón en el lado derecho del controlador de vista y cuando presiona el botón, el PageViewController toma el control y cambia la página.
¿Cómo puedo hacer que el botón reciba el toque y cancelar el reconocedor de gestos en el PageViewController?
Creo que el PageViewController hace que mi ViewController sea una subvista de su vista.
Sé que puedo desactivar todos los gestos, pero este no es el efecto que estoy buscando.
Preferiría no subclasificar el PageViewController ya que Apple dice que esta clase no debe ser subclasificada.
Aquí hay otra solución, que se puede agregar en la plantilla viewDidLoad
justo después de que la parte self.view.gestureRecognizers = self.pageViewController.gestureRecognizers
de la plantilla de Xcode. Evita jugar con las agallas de los reconocedores de gestos o tratar con sus delegados. Simplemente elimina el reconocedor de gestos de toque de las vistas, dejando solo el reconocedor de barrido.
self.view.gestureRecognizers = self.pageViewController.gestureRecognizers;
// Find the tap gesture recognizer so we can remove it!
UIGestureRecognizer* tapRecognizer = nil;
for (UIGestureRecognizer* recognizer in self.pageViewController.gestureRecognizers) {
if ( [recognizer isKindOfClass:[UITapGestureRecognizer class]] ) {
tapRecognizer = recognizer;
break;
}
}
if ( tapRecognizer ) {
[self.view removeGestureRecognizer:tapRecognizer];
[self.pageViewController.view removeGestureRecognizer:tapRecognizer];
}
Ahora, para pasar de una página a otra, debes deslizarla. Los toques ahora solo funcionan en tus controles en la parte superior de la vista de página (que es lo que yo buscaba).
Configurar el gestureRecognizers delegar en viewController como a continuación ya no funciona en ios6
for (UIGestureRecognizer *gR in self.view.gestureRecognizers) {
gR.delegate = self;
}
Creo pageviewcontrollers regularmente a medida que mi usuario salta, se encrespa y se desliza a diferentes vistas de página. En la rutina que crea un nuevo pageviewcontroller, uso una versión ligeramente más simple del excelente código que se muestra arriba:
UIPageViewController *npVC = [[UIPageViewController alloc]
initWithTransitionStyle:UIPageViewControllerTransitionStylePageCurl
navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal
options: options];
...
// Find the pageView tap gesture recognizer so we can remove it!
for (UIGestureRecognizer* recognizer in npVC.gestureRecognizers) {
if ( [recognizer isKindOfClass:[UITapGestureRecognizer class]] ) {
UIGestureRecognizer* tapRecognizer = recognizer;
[npVC.view removeGestureRecognizer:tapRecognizer];
break;
}
}
Ahora los grifos funcionan como deseo (con los grifos izquierdos y derechos saltando una página, y los rizos funcionan bien).
En las versiones más nuevas (estoy en Xcode 7.3 con iOS 8.1), ninguna de estas soluciones parece funcionar.
La respuesta aceptada se bloqueará con error:
El reconocedor de gestos pan integrado de UIScrollView debe tener su vista de desplazamiento como su delegado.
La respuesta de clasificación más alta actualmente (de Pat McG) ya no funciona tan bien porque la vista de UIPageViewController
de UIPageViewController parece estar utilizando sub clases de reconocedores de gestos extraños que no se pueden verificar. Por lo tanto, la instrucción if ( [recognizer isKindOfClass:[UITapGestureRecognizer class]] )
nunca se ejecuta.
Elegí simplemente establecer cancelsTouchesInView en cada reconocedor en falso, lo que permite que las subvistas de UIPageViewController
reciban toques.
En viewDidLoad
:
guard let recognizers = self.pageViewController.view.subviews[0].gestureRecognizers else {
print("No gesture recognizers on scrollview.")
return
}
for recognizer in recognizers {
recognizer.cancelsTouchesInView = false
}
En mi caso, quise deshabilitar el tapping en UIPageControl y dejar que el tapping sea recibido por otro botón en la pantalla. Deslizar todavía funciona. He intentado varias maneras y creo que esa fue la solución de trabajo más simple:
for (UIPageControl *view in _pageController.view.subviews) {
if ([view isKindOfClass:[UIPageControl class]]) {
view.enabled = NO;
}
}
Esto es obtener la vista UIPageControl desde las subvistas UIPageController y deshabilitar la interacción del usuario.
Esta es la solución que funcionó mejor para mí. Probé la respuesta de JRAMER y estaba bien, excepto que obtendría un error al buscar más allá de los límites (página -1 o página 23 para mí).
La solución PatMCG no me dio suficiente flexibilidad ya que canceló todos los taprecognizers, aún quería el tap pero no dentro de mi etiqueta
En mi UILabel simplemente anulé lo siguiente, este canto cancelado solo para mi etiqueta
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
if ([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]) {
return NO;
} else {
return YES;
}
}
Extensión Swift 3 para eliminar el reconocedor de tap:
import UIKit
extension UIPageViewController {
func removeTapRecognizer() {
let gestureRecognizers = self.gestureRecognizers
var tapGesture: UIGestureRecognizer?
gestureRecognizers.forEach { recognizer in
if recognizer.isKind(of: UITapGestureRecognizer.self) {
tapGesture = recognizer
}
}
if let tapGesture = tapGesture {
self.view.removeGestureRecognizer(tapGesture)
}
}
}
Si usa un botón que ha subclasificado, puede anular touchesBegan, touchesMoved y touchesEnded, invocando su propio giro de página programático según corresponda pero sin llamar a super y pasando los toques a la cadena de notificación.
Simplemente cree una subvista (vinculada a un nuevo IBOutlet gesturesView) en su RootViewController y asigne los gestos a esta nueva vista. Esta vista cubre la parte de la pantalla que desea que habilite el gesto.
en el cambio de viewDidLoad:
self.view.gestureRecognizers = self.pageViewController.gestureRecognizers;
a :
self.gesturesView.gestureRecognizers = self.pageViewController.gestureRecognizers;
También puede usar esto (gracias por su ayuda, por ejemplo sobre delegado):
// add UIGestureRecognizerDelegate
NSPredicate *tp = [NSPredicate predicateWithFormat:@"self isKindOfClass: %@", [UITapGestureRecognizer class]];
UITapGestureRecognizer *tgr = (UITapGestureRecognizer *)[self.pageViewController.view.gestureRecognizers filteredArrayUsingPredicate:tp][0];
tgr.delegate = self; // tap delegating
NSPredicate *pp = [NSPredicate predicateWithFormat:@"self isKindOfClass: %@", [UIPanGestureRecognizer class]];
UIPanGestureRecognizer *pgr = (UIPanGestureRecognizer *)[self.pageViewController.view.gestureRecognizers filteredArrayUsingPredicate:pp][0];
pgr.delegate = self; // pan delegating
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
CGPoint touchPoint = [touch locationInView:self.view];
if (UIInterfaceOrientationIsPortrait([[UIApplication sharedApplication] statusBarOrientation]) && touchPoint.y > 915 ) {
return NO; // if y > 915 px in portrait mode
}
if (UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation]) && touchPoint.y > 680 ) {
return NO; // if y > 680 px in landscape mode
}
return YES;
}
Funciona perfectamente para mí :)
Terminé aquí mientras buscaba una manera simple y sencilla de responder a los grifos de mis controles de vista secundarios dentro de UIPageViewController. El núcleo de mi solución (Swift 4, Xcode 9) terminó siendo tan simple como esto, en mi RootViewController.swift
(misma estructura que la plantilla de Xcode basada en la página):
override func viewDidLoad() {
super.viewDidLoad()
...
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(pageTapped(sender:)))
self.pageViewController?.view.subviews[0].addGestureRecognizer(tapGesture)
}
...
@objc func pageTapped(sender: UITapGestureRecognizer) {
print("pageTapped")
}
(También utilicé esta respuesta para permitirme hacer un seguimiento de la página que se tocó, es decir, la actual).
Usted puede anular
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldReceiveTouch:(UITouch *)touch
para controlar mejor cuando el PageViewController debe recibir el toque y no. Consulte "Cómo evitar que los reconocedores de gestos analicen los toques" en Dev API Gesture Recognizers
Mi solución se ve así en RootViewController para UIPageViewController:
En viewDidLoad:
//EDITED Need to take care of all gestureRecogizers. Got a bug when only setting the delegate for Tap
for (UIGestureRecognizer *gR in self.view.gestureRecognizers) {
gR.delegate = self;
}
La anulación:
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
//Touch gestures below top bar should not make the page turn.
//EDITED Check for only Tap here instead.
if ([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]) {
CGPoint touchPoint = [touch locationInView:self.view];
if (touchPoint.y > 40) {
return NO;
}
else if (touchPoint.x > 50 && touchPoint.x < 430) {//Let the buttons in the middle of the top bar receive the touch
return NO;
}
}
return YES;
}
Y no olvide configurar RootViewController como UIGestureRecognizerDelegate.
(Para su información, solo estoy en modo Paisaje).
EDITAR - El código anterior traducido a Swift 2:
En viewDidLoad:
for gr in self.view.gestureRecognizers! {
gr.delegate = self
}
Haga que el controlador de vista de página herede UIGestureRecognizerDelegate
luego agregue:
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
if let _ = gestureRecognizer as? UITapGestureRecognizer {
let touchPoint = touch .locationInView(self.view)
if (touchPoint.y > 40 ){
return false
}else{
return true
}
}
return true
}
Yo tuve el mismo problema. El ejemplo y la documentación hacen esto en loadView o viewDidLoad:
self.view.gestureRecognizers = self.pageViewController.gestureRecognizers;
Esto reemplaza los reconocedores de gestos de las vistas de UIViewControllers con los gestureRecognizers de UIPageViewController. Ahora, cuando se produce un toque, primero se envían a través de los reconocedores de gestos pageViewControllers; si no coinciden, se envían a las subvistas.
Simplemente elimine el comentario de esa línea, y todo está funcionando como se esperaba.
Phillip
solía
for (UIScrollView *view in _pageViewController.view.subviews) {
if ([view isKindOfClass:[UIScrollView class]]) {
view.delaysContentTouches = NO;
}
}
para permitir que los clics pasen a botones dentro de un UIPageViewController