2012-10-05 23 views
9

Quiero implementar la capacidad de navegar hacia atrás y cuarto en mi vista web utilizando el evento swipeWithEvent. Sin embargo, no tengo idea de cómo se supone que debo usar esto.Cómo usar correctamente swipeWithEvent para navegar por una vista web, Obj-C

Tengo una vista web principal, y dos métodos que navegan hacia atrás y en cuarto lugar. Un gran problema aquí es que no estoy exactamente seguro de cómo escribir esta pregunta. Solo necesito saber cómo reconocer los gestos de deslizamiento en mi vista web y llamar a mis dos métodos. Similar a lo que hacen Safari, Google Chrome y Mozilla Firefox. Gracias.

EDIT He implementado estos métodos que se supone que me permiten deslizar hacia adelante y hacia atrás.

- (void)swipeWithEvent:(NSEvent *)event { 
    NSLog(@"Swipe With Event"); 
    CGFloat x = [event deltaX]; 
    //CGFloat y = [event deltaY]; 

    if (x != 0) { 
     (x > 0) ? [self goBack:self] : [self goForward:self]; 
    } 
} 


-(BOOL)recognizeTwoFingerGestures 
{ 
    NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; 
    return [defaults boolForKey:@"AppleEnableSwipeNavigateWithScrolls"]; 
} 

- (void)beginGestureWithEvent:(NSEvent *)event 
{ 
    if (![self recognizeTwoFingerGestures]) 
     return; 

    NSSet *touches = [event touchesMatchingPhase:NSTouchPhaseAny inView:nil]; 

    self.twoFingersTouches = [[NSMutableDictionary alloc] init]; 

    for (NSTouch *touch in touches) { 
     [twoFingersTouches setObject:touch forKey:touch.identity]; 
    } 
} 

- (void)endGestureWithEvent:(NSEvent *)event 
{ 
    if (!twoFingersTouches) return; 

    NSSet *touches = [event touchesMatchingPhase:NSTouchPhaseAny inView:nil]; 

    // release twoFingersTouches early 
    NSMutableDictionary *beginTouches = [twoFingersTouches copy]; 
    self.twoFingersTouches = nil; 

    NSMutableArray *magnitudes = [[NSMutableArray alloc] init]; 

    for (NSTouch *touch in touches) 
    { 
     NSTouch *beginTouch = [beginTouches objectForKey:touch.identity]; 

     if (!beginTouch) continue; 

     float magnitude = touch.normalizedPosition.x - beginTouch.normalizedPosition.x; 
     [magnitudes addObject:[NSNumber numberWithFloat:magnitude]]; 
    } 

    // Need at least two points 
    if ([magnitudes count] < 2) return; 

    float sum = 0; 

    for (NSNumber *magnitude in magnitudes) 
     sum += [magnitude floatValue]; 

    // Handle natural direction in Lion 
    BOOL naturalDirectionEnabled = [[[NSUserDefaults standardUserDefaults] valueForKey:@"com.apple.swipescrolldirection"] boolValue]; 

    if (naturalDirectionEnabled) 
     sum *= -1; 

    // See if absolute sum is long enough to be considered a complete gesture 
    float absoluteSum = fabsf(sum); 

    if (absoluteSum < kSwipeMinimumLength) return; 

    // Handle the actual swipe 
    if (sum > 0) 
    { 
     [self goForward:self]; 
    } else 
    { 
     [self goBack:self]; 
    } 


} 

Sin embargo, este código no está haciendo nada. No parece ser llamado en absoluto.

+0

¿Podría cargar una aplicación de muestra donde '-swipeWithEvent:' no se llama? – Kentzo

+1

Aquí. https://www.dropbox.com/s/sukkzvefwi836kr/swipeProject.zip?dl=1 – Josiah

Respuesta

19

Para sistemas operativos modernos (Lion y más nuevos), este es uno de esos "¿Cómo hago X con Y?" preguntas donde la respuesta es "No usar Y".

-swipeWithEvent: se utiliza para implementar el deslizamiento del panel táctil estilo 10.6, donde el contenido de la pantalla no se mueve con el deslizamiento. La mayoría de los trackpads de Mac no están configurados para permitir este tipo de deslizamiento; la preferencia "Pasar de una página a otra" en el panel de preferencias Trackpad debe configurarse como "deslizar con tres dedos" para que esté disponible, y esa no es la configuración predeterminada ni una configuración común para que los usuarios cambien.

En cambio, los "deslizamientos de fluido" de estilo león entran como eventos de desplazamiento. El proyecto de ejemplo PictureSwiper en su Xcode SDK es un buen punto de partida, sino como una visión general, esto es lo que hace:

Si sólo necesita para apoyar 10.8+

Utilice un NSPageController en el modo de pila del historial .

Si necesita apoyar 10,7

  1. seriamente la posibilidad de apoyar solamente 10.8+, al menos para esta función. Implementarlo manualmente es un gran desastre. No digas que no te advertí.

  2. Cree una vista personalizada que sea una supervista a las vistas que desee deslizar.

  3. Anule -wantsScrollEventsForSwipeTrackingOnAxis: para devolver YES para el eje apropiado. Si está navegando hacia atrás/adelante al estilo Safari, eso es NSEventGestureAxisHorizontal.

  4. Respire hondo; este es un tonto.

  5. Anular -scrollWheel: en su vista personalizada. Su anulación debe llamar al -trackSwipeEventWithOptions:dampenAmountThresholdMin:max:usingHandler:. A grandes rasgos, el umbral mínimo de la cantidad de amortiguación es la cantidad de datos visibles que el usuario puede deslizar hacia la izquierda, y el máximo es cuántos pueden deslizar hacia la derecha. El controlador es un bloque que se llama repetidamente a medida que el usuario pasa; debe:

    1. Publicar un NSImageView con una captura de pantalla de la página que está pasando atrás/adelante a detrás de la vista del contenido.
    2. Mueva la vista de contenido para que coincida con los movimientos del usuario. Tenga en cuenta que el parámetro gestureAmount es, al igual que los umbrales de cantidad de amortiguación, un número (fraccional y posiblemente negativo) de elementos; tienes que multiplicarlo por el ancho de vista para posicionar correctamente la vista de contenido.
    3. Si la fase de gesto es NSEventPhaseEnded, evalúe el gestureAmount para determinar si el usuario completó el gesto. Si no lo hicieron, anime la vista de contenido en su lugar; si lo hicieron, vuelva a colocar la vista de contenido sin animación y actualícela para que coincida con la captura de pantalla.

Como se puede ver, la aplicación real de la manejador es muy complicado, y ni siquiera se han descrito todos los detalles. Incluso con todos estos detalles, un programador altamente calificado probablemente tendrá que pasar unos días haciendo esto correctamente. La muestra de PictureSwiper en el 10.7 SDK es un buen lugar para comenzar. (La versión 10.8 de PictureSwiper utiliza NSPageController Al igual que usted debe hacer..)

+0

Excepto que mi aplicación es compatible con Snow Leopard +. Por lo tanto, NSPageController no puede ser utilizado. – Josiah

+0

@AceLegend: la mayor parte de la respuesta de Brent es cómo hacerlo sin NSPageController (si estuviera utilizando NSPageController, se detendría en el paso 1). Sin embargo, tendrá aún más trabajo por hacer cuando las API 10.7 y posteriores no estén disponibles. –

+0

Ya veo. Quizás podría implementar NSPageController solo para OS X 10.8. Simplemente no soy partidario de usar código específico del sistema operativo. Encuentro que causa problemas. – Josiah

9

De the 10.7 release notes:

Fluid Seguimiento Flagelo - API

La siguiente API permite el seguimiento rueda de desplazamiento gesto eventos como un gesto de deslizamiento fluido. De forma similar a iOS, NSScrollView rebotará una vez si es necesario, luego, opcionalmente, pasará los eventos de la rueda de desplazamiento del gesto para que su controlador pueda usar esta API para seguir el gesto de desplazamiento como un gesto de deslizamiento de fluido.

ScrollWheel NSEventos ahora responden al mensaje de la fase. Hay 3 tipos de pergaminos:

1) Desplazamientos de gestos: comienzan con NSEventPhaseBegan, tienen varios eventos NSEventPhaseChanged y finalizan cuando el usuario levanta los dedos con NSEventPhaseEnded.

2) Desplazamientos de impulso: estos tienen una fase de NSEventPhase ninguno, pero tienen un momentumPhase de NSEventPhaseBegan/NSEventPhaseChanged/NSEventPhaseEnded.

3) Pergaminos heredados: estos eventos de la rueda de desplazamiento tienen una fase de NSEventPhaseNone y una momentumPhase de NSEventPhaseNone. No hay forma de determinar cuándo el usuario inicia, ni se detiene, la realización de pergaminos heredados.

NSScrollView procesa todos los eventos de rueda de desplazamiento de gestos y no los pasa por la cadena de respuesta. A menudo, el seguimiento de un deslizamiento se realiza más alto en la cadena de respuesta, por ejemplo en el nivel WindowController. Para lograr un comportamiento de "rebote cuando no está al borde, de lo contrario deslice" el estilo de iOS, debe informar a NSScrollView que debe reenviar los mensajes de la rueda de desplazamiento cuando corresponda. En lugar de establecer manualmente una propiedad en NSScrollView, su NSResponder puede implementar el siguiente método y devolver SÍ.

- (BOOL)wantsScrollEventsForSwipeTrackingOnAxis:(NSEventGestureAxis)axis; 

Cuando el controlador recibe una adecuada -scrollWheel: mensaje con una fase no NSEventNone, se puede llamar al siguiente mensaje en esa instancia evento para realizar el seguimiento del golpe o desplazarse tanto a la finalización de usuario del evento, y la terminación de animación .

enum { 
    NSEventSwipeTrackingLockDirection = 0x1 << 0, 
    NSEventSwipeTrackingClampGestureAmount = 0x1 << 1 
}; 

typedef NSUInteger NSEventSwipeTrackingOptions; 

@interface NSEvent ... 
- (void)trackSwipeEventWithOptions:(NSEventSwipeTrackingOptions)options 
      dampenAmountThresholdMin:(CGFloat)minDampenThreshold 
           max:(CGFloat)maxDampenThreshold 
         usingHandler:(void (^)(CGFloat gestureAmount, NSEventPhase phase, > BOOL isComplete, BOOL *stop))handler; 
... 

A continuación se muestra un ejemplo de pseudo código de deslizar una colección de imágenes como la aplicación de fotos de iOS.

- (BOOL)wantsScrollEventsForSwipeTrackingOnAxis:(NSEventGestureAxis)axis { 
    return (axis == NSEventGestureAxisHorizontal) ? YES : NO; } 
- (void)scrollWheel:(NSEvent *)event { 
    // NSScrollView is instructed to only forward horizontal scroll gesture events (see code above). However, depending 
    // on where your controller is in the responder chain, it may receive other scrollWheel events that we don't want 
    // to track as a fluid swipe because the event wasn't routed though an NSScrollView first. 
    if ([event phase] == NSEventPhaseNone) return; // Not a gesture scroll event. 
    if (fabsf([event scrollingDeltaX]) <= fabsf([event scrollingDeltaY])) return; // Not horizontal 
    // If the user has disabled tracking scrolls as fluid swipes in system preferences, we should respect that. 
    // NSScrollView will do this check for us, however, depending on where your controller is in the responder chain, 
    // it may scrollWheel events that are not filtered by an NSScrollView. 
    if (![NSEvent isSwipeTrackingFromScrollEventsEnabled]) return; 
    if (_swipeAnimationCanceled && *_swipeAnimationCanceled == NO) { 
     // A swipe animation is still in gestureAmount. Just kill it. 
     *_swipeAnimationCanceled = YES; 
     _swipeAnimationCanceled = NULL; 
    } 
    CGFloat numPhotosToLeft = // calc num of photos we can move to the left and negate 
    CGFloat numPhotosToRight = // calc num of photos we can move to the right 
    __block BOOL animationCancelled = NO; 
    [event trackSwipeEventWithOptions:0 dampenAmountThresholdMin:numPhotosToLeft max:numPhotosToRight 
         usingHandler:^(CGFloat gestureAmount, NSEventPhase phase, BOOL isComplete, BOOL *stop) { 
     if (animationCancelled) { 
      *stop = YES; 
      // Tear down animation overlay 
      return; 
     } 
     if (phase == NSEventPhaseBegan) { 
      // Setup animation overlay layers 
     } 
     // Update animation overlay to match gestureAmount 
     if (phase == NSEventPhaseEnded) { 
      // The user has completed the gesture successfully. 
      // This handler will continue to get called with updated gestureAmount 
      // to animate to completion, but just in case we need 
      // to cancel the animation (due to user swiping again) setup the 
      // controller/view to point to the new content/index/whatever 
     } else if (phase == NSEventPhaseCancelled) { 
      // The user has completed the gesture un-successfully. 
      // This handler will continue to get called with updated gestureAmount 
      // But we don't need to change the underlying controller/view settings. 
     } 
     if (isComplete) { 
      // Animation has completed and gestureAmount is either -1, 0, or 1. 
      // This handler block will be released upon return from this iteration. 
      // Note: we already updated our view to use the new (or same) content 
      // above. So no need to do that here. Just... 
      // Tear down animation overlay here 
      self->_swipeAnimationCanceled = NULL; 
     } 
    }]; 
    // We keep a pointer to a BOOL __block variable so that we can cancel our 
    // block handler at any time. Note: You must assign the BOOL pointer to your 
    // ivar after block creation and copy! 
    self->_swipeAnimationCanceled = &animationCancelled; } 

Otra solución sería tratar directamente los eventos táctiles directamente con el fin de reconocer gestos. Para obtener más información, considere echar un vistazo a documentation.

+1

¿Estoy leyendo esto mal, pero parece que su ejemplo requiere que tenga un NSScrollView. No tengo uno Además, eso parece un poco complicado para lo que necesito. Quiero seguir el deslizamiento, pero realmente solo necesito saber cuándo el usuario "completa" un deslizamiento en cualquier dirección. Entonces solo quiero ejecutar un método. Eso es todo. – Josiah

+1

@AceLegend Estás malinterpretando, no requiere NSScrollView. Toma nota de qué hacer tiene NSScrollView. – Kentzo

+0

De acuerdo, me alegro de eso. Pero hombre, me sorprende lo complicado que es esto. Estoy cruzando los dedos por un reconocedor de gestos para OS X 10.9 SDK. :) – Josiah

Cuestiones relacionadas