2011-01-05 18 views
13

Tengo un UISlider como parte de una vista que se carga en un UIScrollView con la paginación habilitada. He notado un comportamiento inesperado. Si el usuario intenta usar el control deslizante rápidamente (es decir, presionar y mover), "activa" la vista de desplazamiento, haciendo que la página cambie. Sin embargo, si presiona y mantiene presionado por un segundo el control deslizante se "activa" y luego puede ajustar el valor del control deslizante. Este comportamiento no es deseable.UISlider y UIScrollView

¿Cuál es la mejor forma de hacer que el UISlider responda cuando se carga en un UIScrollView? He pensado en agregar una vista de "bloqueador" que simplemente combata los eventos táctiles que se colocan debajo del control deslizante, pero no estoy seguro si esta es la mejor manera de hacerlo.

Respuesta

3

Haga que su vista de desplazamiento detecte los toques sobre la región ocupada por su control deslizante (anule hitTest:withEvent:, puede necesitar la subclase UIScrollView). Si se detecta un toque sobre dicha región, indique a su vista de desplazamiento que pase inmediatamente el control deslizante.

+0

Gracias, terminé yendo con eso. Sin embargo, otra rareza ... este comportamiento solo pareció manifestarse en el simulador. Una vez que lo obtuve en el dispositivo funcionó bien sin la solución. – MarkPowell

+0

Interesante, otra razón para no confiar únicamente en el simulador para probar ... – BoltClock

+2

FWIW, estoy viendo exactamente el mismo comportamiento (como se describe en la pregunta original) tanto en el simulador (iOS 6) como en el dispositivo (iOS 5.1). Tengo que mantener pulsado un momento antes de arrastrar el control deslizante, o si no, obtengo la vista de desplazamiento arrastrar en su lugar. –

40

no es necesario realizar una prueba de detección en el lado UIScrollView. ya que la demora la establece el propio UIScrollView. la creación de subclases y la implementación de un hitTest:withEvent: personalizado no ayudará, ya que aún se desencadena con retraso.

busqué horas para encontrar una solución elegante para esto, ya que quería simular el propio deslizador de volúmenes de Apple en el selector de aplicaciones ios.

el truco:

yourScrollView.delaysContentTouches = NO; 

lamentablemente esto desactiva eventos a lo largo de la pista UISliders, por lo que para esta parte de su UIScrollView no activará ningún touchevents porque están atrapados por el deslizador en primer lugar.

pasar touchevents distintos de los que están en el UISliders pulgar rect tiene que subclase UISlider y añadir lo siguiente:

// get the location of the thumb 
- (CGRect)thumbRect 
{ 
    CGRect trackRect = [self trackRectForBounds:self.bounds]; 
    CGRect thumbRect = [self thumbRectForBounds:self.bounds 
            trackRect:trackRect 
             value:self.value]; 
    return thumbRect; 
} 

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event 
{ 
    CGRect thumbFrame = [self thumbRect]; 

     // check if the point is within the thumb 
    if (CGRectContainsPoint(thumbFrame, point)) 
    { 
     // if so trigger the method of the super class 
     NSLog(@"inside thumb"); 
     return [super hitTest:point withEvent:event]; 
    } 
    else 
    { 
     // if not just pass the event on to your superview 
     NSLog(@"outside thumb"); 
     return [[self superview] hitTest:point withEvent:event]; 
    } 
} 
+0

Gracias Sebastian, ¡eso es exactamente lo que estaba buscando! –

+0

de nada, alex. –

+0

Estaba buscando exactamente lo mismo, ¡gracias! –

0

Usted puede subclase UIScrollView y reemplazar el método - (BOOL)touchesShouldBegin:(NSSet *)touches withEvent:(UIEvent *)event inContentView:(UIView *)view de la siguiente manera:

- (BOOL)touchesShouldBegin:(NSSet *)touches withEvent:(UIEvent *)event inContentView:(UIView *)view { 
    if ([view isKindOfClass:[UISlider class]]) { 
     UITouch *touch = [[event allTouches] anyObject]; 
     CGPoint location = [touch locationInView:view]; 
     CGRect thumbRect; 
     UISlider *mySlide = (UISlider*) view; 
     CGRect trackRect = [mySlide trackRectForBounds:mySlide.bounds]; 
     thumbRect = [mySlide thumbRectForBounds:mySlide.bounds trackRect:trackRect value:mySlide.value]; 
     if (CGRectContainsPoint(thumbRect, location)) 
      return YES; 
    } 
    return NO; 
} 

También establece yourScrollView.delaysContentTouches = NO; propiedad para la vista de desplazamiento.

1

bien la creación de un reproductor de audio que tenía el problema exacto con un sliderView y se añade a una scrollView, y cada vez que tocaba el sliderView, la scrollView utilizado para obtener el tacto y el lugar de la sliderView, la scrollView se movió. Para evitar esto, deshabilité la compilación del scrollView cuando el usuario tocó el sliderView y, de lo contrario, el desplazamiento está habilitado.

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { 

    UIView *result = [super hitTest:point withEvent:event] ; 
    if (result == sliderView) 
    { 
     scrollView.scrollEnabled = NO ; 
    } 
    else 
    { 
     scrollView.scrollEnabled = YES ; 
    } 

    return result ; 

} 
1

Mi problema era un superconjunto de este tema - Tengo UISliders dentro UITableViewCells y todo el UITableView es una página dentro de un UIScrollView. Los controles deslizantes estaban causando estragos en las interacciones con los otros dos y las soluciones de subclases no funcionaron. Esto es lo que se me ocurrió que funciona estupendamente: envíe notificaciones cuando los controles deslizantes se muevan y tenga UITableView y UIScrollView disableScrolling activados durante este tiempo. Nota en la imagen siguiente: mis controles deslizantes son horizontales, mi vista de tabla es vertical y mi UIScrollView tiene páginas horizontales.

UISlider in a UITableView in a UIScrollView

El UITableViewCell recoge acontecimientos para el cursor creado mediante programación-:

self.numberSlider = [[UISlider alloc] init]; 
[self.numberSlider addTarget:self action:@selector(sliderValueChanged:) forControlEvents:UIControlEventValueChanged]; 
[self.numberSlider addTarget:self action:@selector(sliderTouchDown:) forControlEvents:UIControlEventTouchDown]; 
[self.numberSlider addTarget:self action:@selector(sliderTouchUp:) forControlEvents:UIControlEventTouchUpInside]; 
[self.numberSlider addTarget:self action:@selector(sliderTouchUp:) forControlEvents:UIControlEventTouchUpOutside]; 

A los efectos de este tutorial, que sólo se preocupan por toma de contacto y Up:

- (void)sliderTouchDown:(UISlider *)sender 
{ 
    [[NSNotificationCenter defaultCenter] postNotificationName:NOTIFY_SLIDER_TOUCH_BEGAN object:nil]; 
} 

- (void)sliderTouchUp:(UISlider *)sender 
{ 
    [[NSNotificationCenter defaultCenter] postNotificationName:NOTIFY_SLIDER_TOUCH_ENDED object:nil]; 
} 

Ahora, detectamos estas notificaciones en UITableView (tenga en cuenta que la tabla de vista está en un VC, pero estoy seguro de que esto funcionaría si subclá culo al aire):

- (void)viewDidLoad 
{ 
    // other stuff 
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sliderTouchDown:) name:NOTIFY_SLIDER_TOUCH_BEGAN object:nil]; 
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sliderTouchUp:) name:NOTIFY_SLIDER_TOUCH_ENDED object:nil]; 
} 

- (void)sliderTouchDown:(NSNotification *)notify 
{ 
    self.treatmentTableView.scrollEnabled = NO; 
} 

- (void)sliderTouchUp:(NSNotification *)notify 
{ 
    self.treatmentTableView.scrollEnabled = YES; 
} 

y la UIScrollView (igual que arriba, rodeado por un VC):

- (void)viewDidLoad 
{ 
    // other stuff 

    // Register for slider notifications 
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(disableScrolling:) name:NOTIFY_SLIDER_TOUCH_BEGAN object:nil]; 
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(enableScrolling:) name:NOTIFY_SLIDER_TOUCH_ENDED object:nil]; 
} 

- (void)disableScrolling:(NSNotification *)notify 
{ 
    self.scrollView.scrollEnabled = NO; 
} 

- (void)enableScrolling:(NSNotification *)notify 
{ 
    self.scrollView.scrollEnabled = YES; 
} 

Me encantaría oír una solución más elegante, pero este sin duda se hace el trabajo . Cuando utiliza un control deslizante, la tabla y las vistas de desplazamiento se mantienen estables y cuando hace clic fuera de los controles deslizantes, la vista de tabla y desplazamiento se mueven como se esperaba. Además, tenga en cuenta que podría usar instancias no subclasificadas de los 3 componentes en esta solución. ¡Espero que esto ayude a alguien!

1

Quería publicar esto como un comentario, pero mi cuenta no tiene representante, así que tuve que publicar una respuesta completa. La respuesta de user762391 que incluye la anulación de hitTest funciona perfectamente. Sólo quiero añadir que, en lugar de anular hitTest, en su lugar podría anular pointInside: withEvent: así (manteniendo el método thumbRect):

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent * _Nullable)event { 
    return CGRectContainsPoint([self thumbRect], point) 
} 

o en Swift:

var thumbRect: CGRect { 
    return thumbRectForBounds(bounds, trackRect: trackRectForBounds(bounds), value: value) 
} 

override func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool { 
    return thumbRect.contains(point) 
} 
1

más upvoted código comentario en Swift 3 :)

import Foundation 

class UISliderForScrollView:UISlider { 
    var thumbRect:CGRect { 
     let trackRect = self.trackRect(forBounds: bounds) 
     return thumbRect(forBounds: bounds, trackRect: trackRect, value: value) 
    } 

    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { 
     if thumbRect.contains(point) { 
      return super.hitTest(point, with: event) 
     } else { 
      return superview?.hitTest(point, with: event) 
     } 
    } 
}