iPhone: como detectar o fim do arrasto do controle deslizante?


94

Como detectar o evento quando o usuário encerrou o arrasto de um ponteiro deslizante?


1
Use: [slider addTarget: self action: @selector (sliderStoppedDragging :) forControlEvents: UIControlEventTouchUpInside | UIControlEventTouchUpOutside];
— Henry Sou

Respostas:


171

Se você não precisa de nenhum dado entre o arrasto, você deve simplesmente definir:

[mySlider setContinuous: NO];

Dessa forma, você receberá o valueChangedevento apenas quando o usuário parar de mover o controle deslizante.

Versão Swift 5 :

mySlider.isContinuous = false

2
Esta é a melhor resposta para esta pergunta. Eu gostaria que pudéssemos mudar a resposta para isso.
— M. Porooshani

1
i.imgur.com/0Edd2xe.png?1 XCode versão 6.x tem esse recurso de configuração setContinuous através do próprio IDE.
— Abhijeet

1
Sim, a melhor resposta é esta.
— sabiland

1
O Xcode 7.3 Interface Builder tem Events [x] Continuous Updatesconfiguração para UISliderView. Desmarcar isso define continuouscomo falso / NÃO.
— leanne

3
Ótima resposta. Aqui está o código Swift 4:self.mySlider.isContinuous = false
— Marco Weber

115

Você pode adicionar uma ação que leva dois parâmetros, remetente e um evento, para UIControlEventValueChanged:

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

Em seguida, verifique a fase do objeto de toque em seu manipulador:

- (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 e 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
        }
    }
}

Observe que no Interface Builder, ao adicionar uma ação, você também tem a opção de adicionar os parâmetros do remetente e do evento à ação.


1
Funciona perfeitamente! Onde você conseguiu a fonte para isso?
— Roi Mulia

2
obrigado @RoiMulia, não me lembro de onde o vi pela primeira vez, uso sliders há anos. Às vezes, eu o uso para ignorar a alteração em UITouchPhaseEnded, pois o valor tende a pular quando o usuário levanta o dedo.
— Devin Pitcher

2
Lembre-se de definir isContinuous = trueem seu UISlider.
— krlbsk de

2
Esta é uma ótima solução, mas tem um problema com o cancelamento por toque. Apesar de haver um evento "cancelado" no enum touchEvent, ele não é disparado quando o toque é cancelado. Portanto, eu recomendo também adicionar um ouvinte para o evento touchCancel do Slider. Isso deve abranger todas as possibilidades.
— bnussey

2
Vi que às vezes a fase pode ir: começou, estacionou, mudou, mas depois nunca terminou nem cancelou. Isso é problemático para meu caso de uso, pois eu esperava que cada interação terminasse ou fosse cancelada. A correção é observada acima: use um alvo / ação de cancelamento de toque separado.
— Jordan H

71

Eu uso as notificações "Retoque interno" e "Retoque externo".

Interface Builder:

Conecte ambas as notificações no Interface Builder ao seu método de recebimento. O método pode ser assim:

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

Em código:

Se você quiser conectá-lo programaticamente, terá algo assim em seu viewWillAppear (ou onde quer que seja adequado), chame:

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

O método de recebimento seria semelhante a este:

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

19

Como UISlideré uma subclasse de UIControl, você pode definir um alvo e uma ação para ele UIControlEventTouchUpInside.

Se você quiser fazer isso em código, terá a seguinte aparência:

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

Isso enviará uma dragEndedForSlider:mensagem a você quando o toque terminar.

Se quiser fazer isso na ponta, você pode clicar com o botão direito do mouse no controle deslizante para obter o menu de conexões e, em seguida, arrastar do soquete “Retocar dentro” para o objeto de destino.

Você também deve adicionar um alvo e uma ação para UIControlEventTouchUpOutsidee para UIControlEventTouchCancel.


1
@rob mayoff Desculpe, mas você precisa usar UIControlEventTouchUpOutside. Caso contrário, o seletor não será chamado se seu dedo não estiver no controle deslizante quando você terminar de arrastar.
— user3164248

2
O comportamento de UISlidermudou desde que escrevi esta resposta.
— rob mayoff

Pode confirmar que você também precisa registrar um manipulador para UIControlEventTouchUpOutside no iOS 9.
— lms

Eu nunca fui capaz de invocar o UIControlEventTouchCancelhandler no iOS 9. Mas sou capaz de invocar UIControlEventTouchUpInsidee UIControlEventTouchUpOutsideapenas.
— petrsyn

17

Swift 5 e Swift 4

  1. Adicione alvo para eventos touchUpInsidee touchUpOutside. Você pode fazer isso programaticamente ou a partir do storyboard. É assim que você faz isso programaticamente

    slider.addTarget(self, action: #selector(sliderDidEndSliding), for: [.touchUpInside, .touchUpOutside])
  2. Escreva sua função para lidar com o final do arrasto do controle deslizante

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

Olá. Como posso pausar o vídeo quando começar a arrastar?
— kemdo

Isso não detecta se um arrasto ocorreu ou não necessariamente e pode ser acionado simplesmente tocando sem arrastar.
— Chewie The Chorkie

9

Você pode usar:

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

para detectar quando os eventos touchDown e touchUp ocorrem no UISlider



5

Resposta rápida 3

Eu tive o mesmo problema e eventualmente fiz isso funcionar, então talvez ajude alguém. Conecte your_Slider a 'Value Changed' no storyboard e quando o slider for movido, "Slider Touched" acionado e quando for solto "Slider Released".

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

2
Boa solução, mas cuidado porque o Slider Released é chamado quando começa a arrastar e termina arrastando
— Guy Daher

e às vezes não é chamado de "Slider Released" no lançamento
— Vadim


4

Swift 3.1

Às vezes, você desejará que os eventos de arrastar do controle deslizante sejam disparados continuamente, mas tem a opção de executar códigos diferentes, dependendo se o controle deslizante ainda está sendo arrastado ou acabou de ser arrastado.

Para fazer isso, expandi a resposta de Andy acima que faz uso da isTrackingpropriedade do controle deslizante . Eu precisava registrar dois estados: sliderHasFinishedTrackinge sliderLastStoredValue.

Meu código corretamente:

  • não dispara uma ação ao iniciar o arrasto

  • dispara a ação se o usuário apenas empurrar o controle deslizante com um único toque (o 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

No Swift 4 você pode usar esta função

1 Primeira etapa: - Adicionando o alvo no controle deslizante

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

2 Segunda etapa: - Criar uma função

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

2

Talvez você deva adicionar destino para eventos UIControlEventTouchUpInside 、 UIControlEventTouchUpOutside 、 UIControlEventTouchCancel.

Eu encontrei o mesmo problema antes, porque eu não adiciono destino para o evento UIControlEventTouchCancel .


1

Tive um problema semelhante recentemente. Aqui está o que eu fiz no Swift:

    theSlider.continuous = false;

O código que contém esse valor contínuo é:

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

O padrão parece ser SIM (verdadeiro). Definir como false faz o que você deseja.


0

Tente assim

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);
}

Isso envia a ação toda vez que o polegar do controle deslizante se move, não apenas quando o arrasto termina.
— rob mayoff

0

RxSwift:

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

0

Crie uma ação e saída para o controle deslizante (ou você pode transformar o remetente da ação para o UISlider) e, na IU, desmarque a caixa de seleção Atualizações Contínuas. Dessa forma, obteremos o valor do controle deslizante apenas quando o controle deslizante parar de arrastar.

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

insira a descrição da imagem aqui

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.