iPhone: ¿Cómo detectar el final del arrastre del control deslizante?


94

¿Cómo detectar el evento cuando el usuario ha terminado de arrastrar un puntero deslizante?


1
Utilice: [slider addTarget: autoacción: @selector (sliderStoppedDragging :) paraControlEvents: UIControlEventTouchUpInside | UIControlEventTouchUpOutside];
Henry Sou

Respuestas:


171

Si no necesita ningún dato entre el arrastre, simplemente debe configurar:

[mySlider setContinuous: NO];

De esta manera, recibirá el valueChangedevento solo cuando el usuario deje de mover el control deslizante.

Versión Swift 5 :

mySlider.isContinuous = false

2
Esta es la mejor respuesta a esta pregunta. Ojalá pudiéramos cambiar la respuesta a esto.
M. Porooshani

1
i.imgur.com/0Edd2xe.png?1 XCode versión 6.x tiene esta característica de configurar setContinuous a través del propio IDE.
Abhijeet

1
Sí, la mejor respuesta es esta.
sabiland

1
Xcode 7.3 Interface Builder tiene Events [x] Continuous Updatesconfiguración para UISliderView. Al desmarcar esto, se establece continuousen falso / NO.
leanne

3
Gran respuesta. Aquí está el código Swift 4:self.mySlider.isContinuous = false
Marco Weber

115

Puede agregar una acción que tome dos parámetros, remitente y un evento, para UIControlEventValueChanged:

[slider addTarget:self action:@selector(onSliderValChanged:forEvent:) forControlEvents:UIControlEventValueChanged]

Luego verifique la fase del objeto táctil en su controlador:

- (void)onSliderValChanged:(UISlider*)slider forEvent:(UIEvent*)event {     
    UITouch *touchEvent = [[event allTouches] anyObject];
    switch (touchEvent.phase) {     
        case UITouchPhaseBegan:
            // handle drag began
            break;
        case UITouchPhaseMoved:
            // handle drag moved
            break;
        case UITouchPhaseEnded:
            // handle drag ended
            break;
        default:
            break;
    }
}

Swift 4 y 5

slider.addTarget(self, action: #selector(onSliderValChanged(slider:event:)), for: .valueChanged)
@objc func onSliderValChanged(slider: UISlider, event: UIEvent) {
    if let touchEvent = event.allTouches?.first {
        switch touchEvent.phase {
        case .began:
            // handle drag began
        case .moved:
            // handle drag moved
        case .ended:
            // handle drag ended
        default:
            break
        }
    }
}

Tenga en cuenta que en Interface Builder al agregar una acción, también tiene la opción de agregar parámetros de remitente y de evento a la acción.


1
¡Funciona perfectamente! ¿De dónde sacaste la fuente para eso?
Roi Mulia

2
gracias @RoiMulia, no recuerdo dónde lo vi por primera vez, lo he estado usando en controles deslizantes durante años. A veces lo uso para ignorar el cambio en UITouchPhaseEnded ya que el valor tiende a saltar cuando el usuario levanta el dedo.
Devin Pitcher

2
Recuerde establecer isContinuous = truesu UISlider.
krlbsk

2
Esta es una gran solución, sin embargo, tiene un problema con la cancelación táctil. A pesar de que hay un evento "cancelado" en la enumeración touchEvent, no se dispara cuando se cancela el toque. Por lo tanto, recomiendo agregar también un oyente para el evento touchCancel desde Slider. Esto debería cubrir todas las posibilidades.
bnussey

2
Vi que a veces la fase puede irse: comenzada, estacionaria, movida, pero luego nunca termina ni cancela. Esto es problemático para mi caso de uso, ya que esperaba que cada interacción terminara o se cancelara. La solución se indica arriba: use un objetivo / acción de cancelación táctil separado.
Jordan H

71

Utilizo las notificaciones "Retocar en el interior" y "Retocar en el exterior".

Constructor de interfaces:

Conecte ambas notificaciones en Interface Builder a su método de recepción. El método podría verse así:

- (IBAction)lengthSliderDidEndSliding:(id)sender {
    NSLog(@"Slider did end sliding... Do your stuff here");
}

En codigo:

Si desea conectarlo programáticamente, tendría algo como esto en su viewWillAppear (o donde se ajuste a usted) llame:

[_mySlider addTarget:self
              action:@selector(sliderDidEndSliding:) 
    forControlEvents:(UIControlEventTouchUpInside | UIControlEventTouchUpOutside)];

El método de recepción se vería así:

- (void)sliderDidEndSliding:(NSNotification *)notification {
     NSLog(@"Slider did end sliding... Do your stuff here");
}

19

Dado que UISlideres una subclase de UIControl, puede establecer un objetivo y una acción para suUIControlEventTouchUpInside .

Si desea hacerlo en código, se ve así:

[self.slider addTarget:self action:@selector(dragEndedForSlider:)
    forControlEvents:UIControlEventTouchUpInside];

Eso te enviará un dragEndedForSlider: mensaje cuando termine el toque.

Si desea hacerlo en su plumilla, puede controlar y hacer clic en el control deslizante para obtener su menú de conexiones y luego arrastrar desde el zócalo "Retocar dentro" hasta el objeto de destino.

También debe agregar un objetivo y una acción para UIControlEventTouchUpOutsidey para UIControlEventTouchCancel.


1
@rob mayoff Lo sentimos, pero es necesario utilizar UIControlEventTouchUpOutside. De lo contrario, no se llamará al selector si su dedo no está en el control deslizante cuando termine de arrastrar.
user3164248

2
El comportamiento de UISliderha cambiado desde que escribí esta respuesta.
rob mayoff

Puede confirmar que no es necesario también registrar un controlador de UIControlEventTouchUpOutside en IOS 9.
LMS

Nunca pude invocar el UIControlEventTouchCancelcontrolador en iOS 9. Pero puedo invocar UIControlEventTouchUpInsidey UIControlEventTouchUpOutsidesolo.
petrsyn

17

Swift 5 y Swift 4

  1. Agregar destino para eventos touchUpInsidey touchUpOutside. Puede hacerlo programáticamente o desde el guión gráfico. Así es como lo haces programáticamente

    slider.addTarget(self, action: #selector(sliderDidEndSliding), for: [.touchUpInside, .touchUpOutside])
  2. Escriba su función para manejar el final del arrastre del control deslizante

    func sliderDidEndSliding() {
        print("end sliding")
    }

Hola. ¿Cómo puedo pausar el video cuando empiezo a arrastrarlo?
kemdo

Esto no detecta si se ha producido un arrastre o no necesariamente, y se puede activar simplemente tocando sin arrastrar.
Chewie The Chorkie

9

Puedes usar:

- (void)addTarget:(id)target action:(SEL)action 
                   forControlEvents:(UIControlEvents)controlEvents  

para detectar cuándo ocurren los eventos touchDown y touchUp en UISlider



5

Respuesta rápida 3

Tuve el mismo problema y finalmente logré que esto funcionara, así que tal vez ayude a alguien. Conecte your_Slider a 'Value Changed' en el guión gráfico y cuando el control deslizante se movió, se activó "Control deslizante tocado" y cuando se soltó "Control deslizante liberado".

@IBAction func your_Slider(_ sender: UISlider) {
    if (yourSlider.isTracking) {
        print("Slider Touched")
    } else {
        print("Slider Released")
    }
}

2
Buena solución, pero cuidado porque Slider Released se llama cuando comienza a arrastrar y termina de arrastrar
Guy Daher

y a veces no se llama "Slider Released" en el lanzamiento
Vadim


4

Swift 3.1

A veces querrá que los eventos de arrastre del control deslizante se activen continuamente, pero tiene la opción de ejecutar un código diferente dependiendo de si el control deslizante todavía se está arrastrando o acaba de terminar de arrastrarse.

Para hacer esto, he ampliado la respuesta de Andy anterior que hace uso de la isTrackingpropiedad del control deslizante . Necesitaba registrar dos estados: sliderHasFinishedTrackingy sliderLastStoredValue.

Mi código correctamente:

  • no dispara una acción al iniciar el arrastre

  • dispara la acción si el usuario solo empuja el control deslizante con un solo toque (lo que significa que isTrackingpermanece false)


class SliderExample: UIViewController {
  var slider: UISlider = UISlider()
  var sliderHasFinishedTracking: Bool = true
  var sliderLastStoredValue: Float!

  override func loadView() {
    super.loadView()

    slider.minimumValue = 100.0
    slider.maximumValue = 200.0
    slider.isContinuous = true
    slider.value = 100.0
    self.sliderLastStoredValue = slider.value
    slider.addTarget(self, action: #selector(respondToSlideEvents), for: .valueChanged)
    // add your slider to the view however you like
  }

  func respondToSlideEvents(sender: UISlider) {
    let currentValue: Float = Float(sender.value)
    print("Event fired. Current value for slider: \(currentValue)%.")

    if(slider.isTracking) {
      self.sliderHasFinishedTracking = false
      print("Action while the slider is being dragged ⏳")
    } else {
      if(self.sliderHasFinishedTracking && currentValue == self.sliderLastStoredValue){
        print("Slider has finished tracking and its value hasn't changed, " +
              "yet event was fired anyway. 🤷") // No need to take action.
      } else {
        self.sliderHasFinishedTracking = true
        print("Action upon slider drag/tap finishing ⌛️")
      }
    }

    self.sliderLastStoredValue = currentValue
  }
}

4

En Swift 4 puedes usar esta función

1 primer paso: - Agregar el objetivo en el control deslizante

self.distanceSlider.addTarget(self, action: #selector(self.sliderDidEndSliding(notification:)), for: ([.touchUpInside,.touchUpOutside]))

2 Segundo paso: - Crear una función

@objc func sliderDidEndSliding(notification: NSNotification)
{
   print("Hello-->\(distanceSlider.value)")
}

2

Tal vez debería agregar destino para eventos UIControlEventTouchUpInside 、 UIControlEventTouchUpOutside 、 UIControlEventTouchCancel.

Encontré el mismo problema antes, porque no agrego objetivo para el evento UIControlEventTouchCancel .


1

Tuve un problema similar recientemente. Esto es lo que hice en Swift:

    theSlider.continuous = false;

El código que contiene este valor continuo dice:

public var continuous: Bool // if set, value change events are generated
    // any time the value changes due to dragging. default = YES

El valor predeterminado parece ser SÍ (verdadero). Establecerlo en falso hace lo que desea.


0

Prueba así

customSlider.minimumValue = 0.0;
    customSlider.maximumValue = 10.0;
[customSlider addTarget:self action:@selector(changeSlider:) forControlEvents:UIControlEventValueChanged];


-(void)changeSlider:(id)sender{
    UISlider *slider=(UISlider *)sender;
    float sliderValue=(float)(slider.value );
NSLog(@"sliderValue = %f",sliderValue);
}

Eso envía la acción cada vez que se mueve el pulgar del control deslizante, no solo cuando el arrastre ha terminado.
rob mayoff

0

RxSwift:

self.slider.rx.controlEvent([.touchUpInside, .touchUpOutside])
    .bind(to: self.viewModel.endSlidingSlider)
    .disposed(by: self.disposeBag)

0

Cree una acción y una salida para el control deslizante (o puede encasillar al remitente de la acción a UISlider) y, en la interfaz de usuario, desmarque la casilla de verificación Actualizaciones continuas. De esta forma obtendremos el valor del control deslizante solo cuando el control deslizante deje de arrastrar.

@IBAction func actionSlider(_ sender: Any) {
   let value = sliderSpeed.value.rounded()
}

ingrese la descripción de la imagen aquí

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.