UISegmentedControl registra los toques en el segmento seleccionado

Tengo un control segmentado donde el usuario puede seleccionar cómo ordenar una lista. Funciona bien.

Sin embargo, me gustaría que cuando se toca un segmento ya seleccionado, la orden se invierta. Tengo todo el código en su lugar, pero no sé cómo registrar los grifos en esos segmentos. Parece que el único evento de control que puede usar es UIControlEventValueChanged, pero eso no funciona (ya que el segmento seleccionado no está cambiando en realidad).

¿Existe alguna solucion para esto? Y si es así, ¿qué es?

¡Gracias por adelantado!

    Puede subclase UISegmentedControl y luego anular setSelectedSegmentIndex:

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

    Si usa IB, asegúrese de configurar la clase de su UISegmentedControl a su subclase.

    Ahora puede escuchar el mismo UIControlEventValueChanged como lo haría normalmente, excepto si el usuario deseleccionó el segmento, verá unSegmentIndex seleccionado 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]); }} 

    Creo que es incluso un poco mejor si usa -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event — ya que este es el comportamiento de UISegmentedControl. Además, parece que no necesita sobrecargar el -(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]; } 

    Quería esto yo mismo. Tomó la solución de Julian (¡gracias!) Y la modificó ligeramente.

    La siguiente subclase UISegmentedControl simplemente desencadena el evento UIControlEventValueChanged incluso cuando el valor no cambió, lo que obviamente se lee un poco raro, pero funciona bien en mi caso y lo mantiene simple.

    AKSegmentedControl.h

     #import  @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 

    Esto funciona tanto en iOS 4 como en 5:

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

    La solución actual presentada todavía no funciona, porque setSelectedSegmentIndex nunca se llama a menos que realmente se toque un nuevo segmento. Al menos en mi caso, esto nunca funcionó, sí uso iOS5, tal vez esta solución funcionó en iOS4. De todos modos, esta es mi solución. Necesita un método de subclase adicional, que es el siguiente:

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

    ¡Buena suerte!

    Esta era una pregunta muy antigua, así que pensé en agregar la solución más simple que usé cuando me encontré con esto en una aplicación iOS 5+.

    Todo lo que hice fue arrastrar un Tap Gesture Recognizer al UISegmentedControl en mi archivo .xib. Compruebe las conexiones de su nuevo reconocedor de gestos para asegurarse de que el control segmentado es el único tomastream de gestureRecognizer y luego simplemente conecte su acción al método que desea disparar en su controlador.

    Este método debe activarse cada vez que toque el control segmentado, de modo que simplemente verifique el índice seleccionado con quizás una variable de instancia para ver si el usuario tocó el segmento ya seleccionado.

     @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 

    En iOS8, todas las respuestas aquí parecen no funcionar o provocar un cambio dos veces. Encontré una solución muy simple, así que me gustaría compartirla.

    En la subclase, solo tengo:

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

    Lo que hace es restablecer el segmento seleccionado a -1 antes de cambiar el segmento de control segmentado. Pasará en el método touchesEnded.

    Esto 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]; } } 

    En Swift 3 podría imaginar al menos 2 soluciones de la siguiente manera. Para un caso especial, también publiqué una tercera solución, donde el segmento seleccionado funciona como botón de alternar.

    Solución 1:

    Sugerencia: ¡Esta solución solo funciona en losSectores de control momentáneos! Si necesita una solución para controles estacionarios, elija la Solución 2

    La idea es registrar un segundo evento que se active en 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") } } } 

    Solución 2: La otra forma es anular las funciones táctiles en UISegmentedControl y disparar el valor valueChanged incluso si el índice del segmento no ha cambiado. Por lo tanto, puede anular el UISegmentedControl siguiente manera:

     // override UISegmentControl to fire event on second hit: class Solution2SegmentControl : UISegmentedControl { private var current:Int = UISegmentedControlNoSegment override func touchesBegan(_ touches: Set, with event: UIEvent?) { current = self.selectedSegmentIndex super.touchesBegan(touches, with: event) } override func touchesEnded(_ touches: Set, 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") } } } 

    Solución 3: si su enfoque era hacer que el segmento seleccionado fuera un botón de alternancia, la Solución 2 podría cambiarse para limpiar el código de esta manera:

     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, with event: UIEvent?) { current = self.selectedSegmentIndex super.touchesBegan(touches, with: event) } override func touchesEnded(_ touches: Set, with event: UIEvent?) { super.touchesEnded(touches, with: event) if shouldToggle, current == self.selectedSegmentIndex { self.selectedSegmentIndex = UISegmentedControlNoSegment } } } 

    Ahora podríamos limpiar la función changeSelection siguiente manera:

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

    Aquí está mi solución. El más elegante, creo, si quieres que el evento ValueChanged se active con cada toque …

    .h

     @interface UISegmentedControlAllChanges : UISegmentedControl @end 

    .metro

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

    La primera idea que tuve fue conectar las acciones Touch Up Inside o Touch Down a tu método de ordenación, pero parece que no funciona.

    La segunda idea es más bien una solución alternativa: establecer la propiedad Momentary en el control segmentado. Esto disparará una acción de Value Did Change cada vez que se toca.

    Gran ayuda! Lo que quiero hacer es tener la opción de configurar uno o ningún botón, y cuando se establece un botón, un segundo toque se desactiva. Esta es mi modificación:

     - (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 primero le permite leer el control para encontrar la configuración actual antes de restablecerla.

    Basado en la respuesta de Bob de Graaf:

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

    Tenga en cuenta el uso de UIControlEventAllTouchEvents lugar de UIControlEventValueChanged .

    Además, no es necesario llamar -(void)setSelectedSegmentIndex:

    Debajo de la pieza de código que funcionó para mí. El toque repetido en el mismo segmento lo deseleccionará.

     @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 

    la solución iOS 9 Anule UISegmentedControl con dicha clase:

     @interface SegmentedControl() @property (nonatomic) NSInteger previousCurrent; @end @implementation SegmentedControl - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { self.previousCurrent = self.selectedSegmentIndex; [super touchesBegan:touches withEvent:event]; } - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { [super touchesEnded:touches withEvent:event]; if (self.selectedSegmentIndex == self.previousCurrent) { [self sendActionsForControlEvents:UIControlEventValueChanged]; } self.previousCurrent = NSNotFound; } @end 

    Estoy usando KVO para invertir el segmento ya seleccionado 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 

    La solución de la respuesta seleccionada no funcionó para mí a partir de iOS v8.x. Pero @Andy ofrece una solución y completaré:

    Subclase el UISegmentedControl y sobrescribe el método touchesEnded en la subclase:

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

    En la clase en la que pretendes utilizar UISegmentedControl, crea un iVar currentSelectedSegIndex. Agregue UIControlEventAllTouchEvents junto a sus métodos de acción UIControlEventValueChanged:

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

    Implementa los dos métodos de acción:

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