Pergunta

Eu tenho um controle segmentado, onde o usuário pode selecionar como encomendar uma lista. Funciona bem.

No entanto, gostaria que, quando um segmento já selecionado é aproveitado, a ordem se inverte. Eu tenho todo o código no lugar, mas eu não sei como registrar as torneiras nesses segmentos. Parece que o único evento de controle que você pode usar é UIControlEventValueChanged, mas que não está funcionando (uma vez que o segmento selecionado não está realmente mudando).

Existe uma solução para isso? E se assim for, o que é?

Agradecemos antecipadamente!

Foi útil?

Solução

Você pode subclasse UISegmentedControl, e depois override setSelectedSegmentIndex:

- (void) setSelectedSegmentIndex:(NSInteger)toValue {
    if (self.selectedSegmentIndex == toValue) {
        [super setSelectedSegmentIndex:UISegmentedControlNoSegment];
    } else {
        [super setSelectedSegmentIndex:toValue];        
    }
}

Se estiver usando IB, certifique-se de definir a classe de seu UISegmentedControl a sua subclasse.

Agora você pode ouvir a mesma UIControlEventValueChanged como faria normalmente, exceto se o usuário desmarcada do segmento, você verá uma selectedSegmentIndex igual a UISegmentedControlNoSegment:

-(IBAction) valueChanged: (id) sender {
    UISegmentedControl *segmentedControl = (UISegmentedControl*) sender;
    switch ([segmentedControl selectedSegmentIndex]) {
        case 0:
            // do something
            break;
        case 1:
            // do something
            break;
        case UISegmentedControlNoSegment:
            // do something
            break;
        default:
            NSLog(@"No option for: %d", [segmentedControl selectedSegmentIndex]);
    }
}

Outras dicas

Eu acho que é até um pouco melhor se você usar -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event --- como este é o comportamento de UISegmentedControl. Além disso, parece que você não precisa sobrecarregar o -(void)setSelectedSegmentIndex:(NSInteger)toValue

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {    
    NSInteger current = self.selectedSegmentIndex;
    [super touchesBegan:touches withEvent:event];
    if (current == self.selectedSegmentIndex) 
        [self sendActionsForControlEvents:UIControlEventValueChanged];
}

Procurado isso mesmo. Tomou solução de Julian (graças!) E ligeiramente modificado.

O UISegmentedControl seguinte subclasse simplesmente dispara o evento UIControlEventValueChanged mesmo quando o valor não se alterou, o que obviamente lê um pouco estranho, mas funciona bem no meu caso e mantém as coisas simples.

AKSegmentedControl.h

#import <UIKit/UIKit.h>

@interface AKSegmentedControl : UISegmentedControl {
}

@end

AKSegmentedControl.m

#import "AKSegmentedControl.h"

@implementation AKSegmentedControl

- (void)setSelectedSegmentIndex:(NSInteger)toValue {
  // Trigger UIControlEventValueChanged even when re-tapping the selected segment.
  if (toValue==self.selectedSegmentIndex) {
    [self sendActionsForControlEvents:UIControlEventValueChanged];
  }
  [super setSelectedSegmentIndex:toValue];        
}

@end

Isso funciona em ambos iOS 4 e 5:

-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
  int oldValue = self.selectedSegmentIndex;
  [super touchesBegan:touches withEvent:event];
  if ( oldValue == self.selectedSegmentIndex )
    [self sendActionsForControlEvents:UIControlEventValueChanged];
}

A solução atual ainda apresentada não trabalho, porque setSelectedSegmentIndex nunca é chamado a não ser realmente um novo segmento é aproveitado. Pelo menos no meu caso, isso nunca funcionou, eu uso iOS5 embora, talvez esta solução operavam em iOS4. De qualquer forma, esta é a minha solução. Ele precisa de um método da subclasse extra, que é o seguinte:

-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{    
    [self setSelectedSegmentIndex:self.selectedSegmentIndex];
    [super touchesEnded:touches withEvent:event];
}

Boa sorte!

Esta foi uma pergunta muito velho, então eu pensei que eu iria adicionar a solução bastante simples que eu usei quando eu corri para este em um iOS 5+ aplicativo.

Tudo que eu fiz foi arrastar um Tap Gesto reconhecedor para o UISegmentedControl no meu arquivo .xib. Verifique as ligações para o seu novo gesto de reconhecimento para garantir que o controle segmentado é a única saída GestureRecognizer e depois é só conectar-se a sua ação para o método que você deseja fogo em seu controlador.

Este método deve disparar a qualquer momento que você tocar o controle segmentado, de modo que basta verificar o índice selecionado talvez com uma variável de instância para ver se o usuário tocou o segmento já selecionado.

@implementation MyViewController

@synthesize selectedSegment;
@synthesize segmentedControl;

// connected to Tap Gesture Recognizer's action
- (IBAction)segmentedControlTouched:(id)sender
{
    NSLog(@"Segmented Control Touched");
    if (selectedSegment == [segmentedControl selectedSegmentIndex]) {
        // Do some cool stuff
    }

    selectedSegment = [segmentedControl selectedSegmentIndex];

}

@end

Em iOS8 cada resposta aqui parece ou não trabalho ou mudança gatilho duas vezes. Eu vim com uma solução muito simples - então eu gostaria de compartilhá-lo

.

Em subclasse eu só tenho:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    [super touchesBegan:touches withEvent:event];
    [self setSelectedSegmentIndex:UISegmentedControlNoSegment];
}

O que acontece é a repor segmento seleccionado a -1 antes de mudar de segmento de controlo segmentada. Ela vai acontecer no método touchesEnded.

Isso funciona:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    int oldValue = self.selectedSegmentIndex;
    [super touchesBegan:touches withEvent:event];
    if (oldValue == self.selectedSegmentIndex)
    {
        [super setSelectedSegmentIndex:UISegmentedControlNoSegment];
        [self sendActionsForControlEvents:UIControlEventValueChanged];
    }
}

Swift 3 eu poderia imaginar pelo menos 2 soluções como segue. Para um caso especial que eu postei também uma terceira solução, onde o segmento selecionado funciona como botão de alternância.

Solução 1:

Dica: Esta solução só funciona em controlSegments momentâneas! Se você precisa de uma solução para controles estacionárias, escolha Solução 2

A idéia é registrar um segundo evento que dispara acima em touch-up-inside:

// this solution works only for momentary segment control:
class Solution1ViewController : UIViewController {
    var current:Int = UISegmentedControlNoSegment
    @IBOutlet weak var mySegmentedControl: UISegmentedControl! {
        didSet {
            guard mySegmentedControl != nil else { return }

            self.mySegmentedControl!.addTarget(
                self,
                action:#selector(changeSelection(sender:)),
                for: .valueChanged)

            self.mySegmentedControl!.addTarget(
                self,
                action:#selector(changeSelection(sender:)),
                for: .touchUpInside)
        }
    }

    func changeSelection(sender: UISegmentedControl) {
        if current == sender.selectedSegmentIndex {
            // user hit the same button again!
            print("user hit the same button again!")
        }
        else {
            current = sender.selectedSegmentIndex
            // user selected a new index
             print("user selected a new index")
        }
    }
}

Solução 2: A outra maneira é para substituir as funções de toque em UISegmentedControl e para disparar o valueChanged mesmo se o índice do segmento não mudou. Portanto, você poderia substituir o UISegmentedControl da seguinte forma:

// override UISegmentControl to fire event on second hit:
class Solution2SegmentControl : UISegmentedControl
{
    private var current:Int = UISegmentedControlNoSegment
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        current = self.selectedSegmentIndex
        super.touchesBegan(touches, with: event)
    }
    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesEnded(touches, with: event)
        if current == self.selectedSegmentIndex {
            self.sendActions(for: .valueChanged)
        }
    }
}

// solution2 works with stationary (default) segment controls
class Solution2ViewController : UIViewController {
    var current:Int = UISegmentedControlNoSegment

    @IBOutlet weak var mySegmentedControl: UISegmentedControl! {
        didSet {
            guard mySegmentedControl != nil else { return }

            self.mySegmentedControl!.addTarget(
                self,
                action:#selector(changeSelection(sender:)),
                for: .valueChanged)
        }
    }

    func changeSelection(sender: UISegmentedControl) {
        if current == sender.selectedSegmentIndex {
            // user hit the same button again!
            print("user hit the same button again!")
        }
        else {
            current = sender.selectedSegmentIndex
            // user selected a new index
            print("user selected a new index")
        }
    }
}

Solução 3: Se a sua abordagem era ter o segmento selecionado ser um botão de alternância de Solução 2 pode ser alterado para limpar o código como este:

class MyToggleSegmentControl : UISegmentedControl {
    /// you could enable or disable the toggle behaviour here
    var shouldToggle:Bool = true

    private var current:Int = UISegmentedControlNoSegment
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        current = self.selectedSegmentIndex
        super.touchesBegan(touches, with: event)
    }
    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesEnded(touches, with: event)
        if shouldToggle, current == self.selectedSegmentIndex {
            self.selectedSegmentIndex = UISegmentedControlNoSegment
        }
    }
}

Agora nós podemos limpar a função changeSelection da seguinte forma:

func changeSelection(sender: UISegmentedControl) {
    switch sender.selectedSegmentIndex {
    case UISegmentedControlNoSegment:
        print("none")
    default:
        print("selected: \(sender.selectedSegmentIndex)")
    }
}

Aqui está a minha solução. O mais elegante que eu acho que se você deseja que o evento ValueChanged ao fogo em todos os toques ...

.h

@interface UISegmentedControlAllChanges : UISegmentedControl
@end

.m

@implementation UISegmentedControlAllChanges

-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{    
    [self sendActionsForControlEvents:UIControlEventValueChanged];
    [super touchesEnded:touches withEvent:event];
}

@end

A primeira ideia que tive foi de arame até os Touch Up Inside ou Touch Down ações para o seu método de classificação, mas isso não parece trabalho.

A segunda idéia é mais um trabalho em torno, defina a propriedade Momentary no controle segmentado. Este, então, disparar uma ação Value Did Change cada vez que é tocada.

Big ajuda! O que eu quero fazer é ter a opção de um ou nenhum botão set - e quando um botão é definida, um segundo toque desactiva-o. Esta é a minha modificação:

- (void)setSelectedSegmentIndex:(NSInteger)toValue
{
  // Trigger UIControlEventValueChanged even when re-tapping the selected segment.
  if (toValue==self.selectedSegmentIndex) {
    [super setSelectedSegmentIndex:UISegmentedControlNoSegment]; // notify first
    [self sendActionsForControlEvents:UIControlEventValueChanged]; // then unset
  } else {
    [super setSelectedSegmentIndex:toValue]; 
  }
}

Notificar primeiro permite que você leia o controle para encontrar a configuração atual antes que seja reiniciado.

Com base na resposta de Bob de Graaf:

-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self sendActionsForControlEvents:UIControlEventAllTouchEvents];
    [super touchesEnded:touches withEvent:event];
}

Observe o uso de UIControlEventAllTouchEvents vez de UIControlEventValueChanged.

Além disso, não há necessidade de -(void)setSelectedSegmentIndex: chamada.

Abaixo o pedaço de código que fez o trabalho para mim. tap repetido no mesmo segmento vai desmarcá-lo.

@implementation WUUnselectableSegmentedControl
{
    NSUInteger _selectedIndexBeforeTouches;
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    _selectedIndexBeforeTouches = self.selectedSegmentIndex;
    [super touchesBegan:touches withEvent:event];
}

-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    [super touchesEnded:touches withEvent:event];

    if (_selectedIndexBeforeTouches == self.selectedSegmentIndex)
    {
        // Selection didn't change after touch - deselect
        self.selectedSegmentIndex = UISegmentedControlNoSegment;
        [self sendActionsForControlEvents:UIControlEventValueChanged];
    }
}

@end

iOS 9 solução. Substituir UISegmentedControl com essa classe:

@interface SegmentedControl()

@property (nonatomic) NSInteger previousCurrent;

@end

@implementation SegmentedControl

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    self.previousCurrent = self.selectedSegmentIndex;
    [super touchesBegan:touches withEvent:event];
}

- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    [super touchesEnded:touches withEvent:event];

    if (self.selectedSegmentIndex == self.previousCurrent) {
        [self sendActionsForControlEvents:UIControlEventValueChanged];
    }

    self.previousCurrent = NSNotFound;
}

@end

Parece uma pergunta divertida de resposta. Este esquema expande soluções yershuachu de Steve E de e. Esta versão utiliza um UITapGestureRecognizer para capturar todos os toques e que define o selectedSegmentIndex para -1; mas também passa em todos os toques para o UISegmentedControl para que ele possa lidar com quaisquer toques normais. Não é necessária nenhuma subclasse.

UISegmentedControl *mySegControl;

- (void)viewDidLoad
{
    [super viewDidLoad];

    [mySegControl addTarget:self action:@selector(segmentAction:)
           forControlEvents:UIControlEventValueChanged];
    // allow the a seg button tap to be seen even if already selected
    UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc]
                                          initWithTarget:self action:@selector(unitsSegTap:)];
    tapGesture.cancelsTouchesInView = NO; // pass touches through for normal taps
    [mySegControl addGestureRecognizer:tapGesture];

    // continue setup ...
}

- (void)segTap:(UITapGestureRecognizer *)sender
{
    mySegControl.selectedSegmentIndex = -1;
}

// called for UIControlEventValueChanged
- (void)segmentAction:(UISegmentedControl *)sender
{
    NSInteger index = sender.selectedSegmentIndex;
    NSLog(@"unitsSegmentAction %d",(int)index);
    // process the segment
}

Eu estou usando KVO para inverter segmento já selecionados para iOS8.

#import "QCSegmentedControl.h"

static void *QCSegmentedControlContext = &QCSegmentedControlContext;

@implementation QCSegmentedControl

- (id)initWithCoder:(NSCoder *)aDecoder {
    self = [super initWithCoder:aDecoder];
    if (self) {
        [self registerKVO];
    }

    return self;
}

- (void)dealloc {
    [self removeObserver:self forKeyPath:@"selectedSegmentIndex"];
}

#pragma mark -

- (void)registerKVO {
    [self addObserver:self
           forKeyPath:@"selectedSegmentIndex"
              options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld)
              context:QCSegmentedControlContext];
}

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context {
    if (context == QCSegmentedControlContext) {
        NSNumber *new = change[NSKeyValueChangeNewKey];
        NSNumber *old = change[NSKeyValueChangeOldKey];
        if (new.integerValue == old.integerValue) {
            self.selectedSegmentIndex = UISegmentedControlNoSegment;
        }
    }
}

@end

A solução da resposta selecionada não funcionou para mim como de corrente v8.x. iOS Mas @Andy oferecer uma solução e vou completar:

subclasse o UISegmentedControl e método touchesEnded de substituição na subclasse:

-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self sendActionsForControlEvents:UIControlEventAllTouchEvents];
    [super touchesEnded:touches withEvent:event];
}

Na classe onde você pretende usar o UISegmentedControl, criar um iVar currentSelectedSegIndex. Adicione os UIControlEventAllTouchEvents ao lado de seus métodos de ação UIControlEventValueChanged:

NSInteger currentSelectedSegIndex;

[aSegmentedController addTarget:self action:@selector(aSegmentedControllerSelection:) forControlEvents:UIControlEventValueChanged];
[aSegmentedController addTarget:self action:@selector(aSegmentedControllerAllTouchEvent:) forControlEvents:UIControlEventAllTouchEvents];

Implementar os dois métodos de ação:

-(void)aSegmentedControllerAllTouchEvent:(MyUISegmentedControl *)seg
{
    seg.selectedSegmentIndex = UISegmentedControlNoSegment;
}
-(void)aSegmentedControllerSelection:(MyUISegmentedControl *)seg
{
    if (currentSelectedSegIndex == seg.selectedSegmentIndex)
    {
        seg.selectedSegmentIndex = UISegmentedControlNoSegment;
        currentSelectedSegIndex = NSIntegerMax;
        NSLog(@"%s Deselected.", __PRETTY_FUNCTION__);
        // do your stuff if deselected
        return;
    }
    NSLog(@"%s Selected index:%u", __PRETTY_FUNCTION__, seg.selectedSegmentIndex);
    currentSelectedSegIndex = seg.selectedSegmentIndex;
    //do your stuff if selected index
}

Isso deve funcionar:

- (IBAction)segmentedControlValueChanged:(id)sender
{
    UISegmentedControl *sc = (UISegmentedControl*) sender;
    switch ([sc selectedSegmentIndex])
    {
       case 0:
            NSLog(@"First option!");
            break;
       case 1:
            NSLog(@"Second option!");
            break;
       case UISegmentedControlNoSegment:
            NSLog(@"No option...");
            break;
        default:
            NSLog(@"No selected option :( ");
    }

    //this resets the segmented control (mySC) value to act as a button.
    [self.mySC setSelectedSegmentIndex:UISegmentedControlNoSegment];
}

Lembre-se conectar este IBAction ao seu Value Changed tomada de controle segmentada nos Conexões Inspector .

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top