Pregunta

Tengo un UIScrollView con 2 páginas, y puedo desplazarse horizontalmente entre ellos. Sin embargo, en una de mis páginas, tengo un UIDatePicker, y la vista de desplazamiento está interceptando los eventos táctiles verticales así que ya no puedo manipular el selector de fechas (excepto haciendo clic o tocando). ¿Hay alguna manera de decirle al ScrollView para enviar los eventos táctiles verticales para el selector de fechas, pero envía los sucesos táctiles horizontales a la vista de desplazamiento para cambiar de página?

¿Fue útil?

Solución

Creo que hay dos partes en este problema. La primera es la determinación de la intención del usuario, y el segundo es conseguir el correcto control para responder a esa intención.

determinar la intención

Creo que es importante ser claro acerca de lo que el usuario tiene la intención. Imagínese este escenario: El usuario inicia tocando la pantalla y mueve su dedo hacia la izquierda, sino también un poco. El usuario probablemente la intención de desplazar la vista, y no tiene intención de cambiar la fecha en absoluto. Sería malo para desplazarse tanto la vista y cambiar la fecha, especialmente justo cuando se mueve fuera de la pantalla. Así que para determinar lo que el usuario tiene la intención sugiero el siguiente algoritmo:

Cuando el usuario inicia tocando la pantalla, grabar la posición inicial. A medida que el dedo del usuario comienza a alejarse de esa posición, los controles no deben reaccionar en absoluto. Una vez que el toque se mueve más allá de un cierto umbral de distancia desde la posición de partida, determinar si se movía más horizontal o verticalmente. Si se mueve verticalmente, el usuario tiene la intención de cambiar la fecha, así que ignore la parte horizontal del movimiento y sólo cambiar la fecha. Si se movía más horizontalmente, el usuario tiene la intención de desplazar la vista, así que ignore la parte vertical del movimiento y sólo desplazar la vista.

Aplicación

Con el fin de implementar esto, usted necesita para manejar los eventos antes de que el selector de fechas o UIScrollView hacer. Probablemente hay algunas maneras de hacer esto, pero uno en particular viene a la mente: hacer una UIView personalizada llamada ScrollingDateMediatorView. Ajuste el UIScrollView como un hijo de este punto de vista. Anular del ScrollingDateMediatorView hitTest: withEvent: y pointInside: withEvent: métodos. Estos métodos necesitan para llevar a cabo el mismo tipo de prueba de posicionamiento que ocurriría normalmente, pero si el resultado es el selector de fecha, lugar de retorno automático. Este secuestra efectivamente eventos táctiles que estaban destinados para el selector de fecha, lo que permite la ScrollingDateMediatorView manejarlos primero. A continuación, se implementa el algoritmo descrito anteriormente en los diversos toques * métodos. Específicamente:

En el touchesBegan:. Método withEvent, guardar la posición de partida

En touchesMoved: withEvent, si la intención del usuario no se conoce, sin embargo, determinar si el tocado se ha movido lo suficientemente lejos de la posición de partida. Si es así, determinar si el usuario tiene la intención de desplazarse o cambiar la fecha y guardar esa intención. Si la intención del usuario ya se conoce y es para cambiar la fecha, enviar el selector de fechas del touchedMoved: withEvent mensaje, de lo contrario enviar el UIScrollView la touchesMoved: withEvent mensaje. Vas a tener que hacer algún trabajo dentro simliar touchesEnded: withEvent y touchesCancelled: withEvent para asegurarse de que las otras vistas reciben los mensajes apropiados. Ambos métodos deben restablecer los valores guardados.

Una vez que lo tienes propagar adecuadamente los acontecimientos, es probable que tenga que probar algunas pruebas de usuario para ajustar el umbral de movimiento.

Otros consejos

En realidad, hay una aplicación mucho más simple que lo que sugirió Bob. Esto funciona perfectamente para mi. Será necesario que su subclase UIScrollView si no lo ha hecho, e incluir este método: -

- (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    UIView* result = [super hitTest:point withEvent:event];

    if ([result.superview isKindOfClass:[UIPickerView class]])
    {
         self.canCancelContentTouches = NO;  
         self.delaysContentTouches = NO;
    }
    else 
    {
         self.canCancelContentTouches = YES; // (or restore bool from prev value if needed)
         self.delaysContentTouches = YES;    // (same as above)
    }
    return result;
}

La razón por la que uso result.superview es que la visión que consiga los toques en realidad será un UIPickerTable, que es una API privada.

Saludos

ayuda impresionante Sam! Tenía que crear una categoría simple que swizzles, el método (porque yo estaba haciendo esto en un UITableViewController y por lo tanto habría tenido que hacer algunas cosas muy sucia a subclase el punto de vista de desplazamiento).

#import <UIKit/UIKit.h>

@interface UIScrollView (withControls)

+ (void) swizzle;

@end

Y el código principal:

#import </usr/include/objc/objc-class.h>
#import "UIScrollView+withControls.h"

#define kUIViewBackgroundImageTag 6183746
static BOOL swizzled = NO;

@implementation UIScrollView (withControls)

+ (void)swizzleSelector:(SEL)orig ofClass:(Class)c withSelector:(SEL)new;
{
    Method origMethod = class_getInstanceMethod(c, orig);
    Method newMethod = class_getInstanceMethod(c, new);

    if (class_addMethod(c, orig, method_getImplementation(newMethod),
                        method_getTypeEncoding(newMethod))) {
        class_replaceMethod(c, new, method_getImplementation(origMethod),
                            method_getTypeEncoding(origMethod));
    } else {
        method_exchangeImplementations(origMethod, newMethod);
    }
}

+ (void) swizzle {
    @synchronized(self) {
        if (!swizzled) {

            [UIScrollView swizzleSelector:@selector(hitTest:withEvent:)
                                  ofClass:[UIScrollView class]
                             withSelector:@selector(swizzledHitTest:withEvent:)];
            swizzled = YES;
        }
    }
}

- (UIView*)swizzledHitTest:(CGPoint)point withEvent:(UIEvent *)event {
    UIView* result = [self swizzledHitTest:point withEvent:event]; // actually calling the original hitTest method

    if ([result.superview isKindOfClass:[UIPickerView class]]) {
        self.canCancelContentTouches = NO;  
        self.delaysContentTouches = NO;
    } else {
        self.canCancelContentTouches = YES; // (or restore bool from prev value if needed)
        self.delaysContentTouches = YES;    // (same as above)
    }
    return result;
}

@end

Entonces, en mi método viewDidLoad, acabo de llamar a

[UIScrollView swizzle];
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top