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?

DroidHeaven
fonte
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
Rok Jarc
fonte
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.

Devin Pitcher
fonte
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");
}
Thomas
fonte
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.

rob mayoff
fonte
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")
    }
Guy Daher
fonte
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

Mudit Bajpai
fonte
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")
    }
}
Andy
fonte
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

Conectar Touch Up InsideeValue Changed

insira a descrição da imagem aqui

Abo3atef
fonte
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
  }
}
Jamie Birch
fonte
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)")
}
Pansora Abhay
fonte
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 .

Zhuoming
fonte
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.

Evdzhan Mustafa
fonte
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);
}
Raj Subbiah
fonte
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)
Adam Smaka
fonte
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

Sameer Jagtap
fonte