UISegmentedControl registra i tocchi sul segmento selezionato

Ho un controllo segmentato in cui l’utente può selezionare come ordinare un elenco. Funziona bene.

Tuttavia, mi piacerebbe che quando un segmento già selezionato viene TAPpato, l’ordine viene invertito. Ho tutto il codice in atto, ma non so come registrare i tap su quei segmenti. Sembra che l’unico evento di controllo che puoi utilizzare sia UIControlEventValueChanged, ma non funziona (poiché il segmento selezionato non sta effettivamente cambiando).

C’è una soluzione per questo? E se sì, cos’è?

Grazie in anticipo!

    È ansible creare sottoclass UISegmentedControl e quindi eseguire l’override di setSelectedSegmentIndex:

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

    Se utilizzi IB, assicurati di impostare la class di UISegmentedControl nella sottoclass.

    Ora puoi ascoltare lo stesso UIControlEventValueChanged come faresti normalmente, tranne se l’utente ha deselezionato il segmento, vedrai un objectSegmentIndex uguale 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]); }} 

    Penso che sia anche un po ‘meglio se usi -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event — come questo è il comportamento di UISegmentedControl. Inoltre, sembra che non sia necessario sovraccaricare il -(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]; } 

    Volevo questo me stesso. Preso soluzione di Julian (grazie!) E leggermente modificato.

    La seguente sottoclass UISegmentedControl semplicemente triggers l’evento UIControlEventValueChanged anche quando il valore non cambia, che ovviamente si legge un po ‘strano, ma nel mio caso funziona bene e semplifica le cose.

    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 

    Funziona su 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]; } 

    La soluzione attuale presentata ancora non funziona, perché setSelectedSegmentIndex non viene mai chiamato a meno che non venga toccato realmente un nuovo segmento. Almeno nel mio caso questo non ha mai funzionato, però io uso iOS5, forse questa soluzione funzionava su iOS4. Ad ogni modo, questa è la mia soluzione. Ha bisogno di un metodo di sottoclass extra, che è il seguente:

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

    In bocca al lupo!

    Questa è stata una domanda piuttosto vecchia quindi ho pensato di aggiungere la soluzione piuttosto semplice che ho utilizzato quando ho trovato questo in un’applicazione iOS 5+.

    Tutto ciò che ho fatto è stato trascinare un Tap Gesture Recognizer su UISegmentedControl nel mio file .xib. Controlla le connessioni per il tuo nuovo riconoscimento dei gesti per assicurarti che il controllo segmentato sia l’unica presa di gestureRecognizer e quindi leghi semplicemente l’azione al metodo che vuoi triggersre nel controller.

    Questo metodo dovrebbe triggersrsi ogni volta che si tocca il controllo segmentato, quindi basta controllare l’indice selezionato con forse una variabile di istanza per vedere se l’utente ha toccato il segmento già selezionato.

     @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 

    Su iOS8 ogni risposta sembra non funzionare o innescare il cambiamento due volte. Ho trovato una soluzione molto semplice – quindi mi piacerebbe condividerla.

    Nella sottoclass ho solo:

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

    Quello che fa è ripristinare il segmento selezionato a -1 prima di cambiare segmento di controllo segmentato. Accadrà nel metodo touched End.

    Questo funziona:

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

    In Swift 3 potrei immaginare almeno 2 soluzioni come segue. Per un caso speciale ho pubblicato anche una terza soluzione, in cui il segmento selezionato funziona come pulsante di triggerszione / distriggerszione.

    Soluzione 1:

    Suggerimento: questa soluzione funziona solo su dispositivi di controllo momentanei! Se hai bisogno di una soluzione per controlli fissi, scegli Soluzione 2

    L’idea è di registrare un secondo evento che si accende sul 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") } } } 

    Soluzione 2: l’altro modo è sostituire le funzioni touch in UISegmentedControl e UISegmentedControl il valueChanged anche se l’indice del segmento non è cambiato. Pertanto è ansible sovrascrivere UISegmentedControl come segue:

     // 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") } } } 

    Soluzione 3: se il tuo approccio prevedeva che il segmento selezionato fosse un pulsante di commutazione, la Soluzione 2 potrebbe essere modificata per pulire il codice in questo modo:

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

    Ora potremmo ripulire la funzione changeSelection come segue:

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

    Ecco la mia soluzione. Il più elegante penso che se vuoi che l’evento ValueChanged esploda su ogni touch …

    .h

     @interface UISegmentedControlAllChanges : UISegmentedControl @end 

    .m

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

    La prima idea che avevo era colbind le azioni Touch Up Inside o Touch Down al tuo metodo di ordinamento, ma questo non sembra funzionare.

    La seconda idea è più una soluzione, impostare la proprietà Momentary sul controllo segmentato. In questo modo, verrà Value Did Change un’azione di Value Did Change ogni volta che viene toccata.

    Grande aiuto! Quello che voglio fare è avere l’opzione di uno o nessun set di pulsanti – e quando un pulsante è impostato, un secondo touch lo distriggers. Questa è la mia modifica:

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

    Notifica per prima cosa consente di leggere il controllo per trovare l’impostazione corrente prima che venga ripristinata.

    Sulla base della risposta di Bob de Graaf:

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

    Notare l’uso di UIControlEventAllTouchEvents invece di UIControlEventValueChanged .

    Inoltre non è necessario chiamare -(void)setSelectedSegmentIndex:

    Sotto il pezzo di codice che ha funzionato per me. Il touch ripetuto sullo stesso segmento lo deselezionerà.

     @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 

    soluzione iOS 9 Sovrascrivi UISegmentedControl con tale class:

     @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 

    Sto usando KVO per invertire il segmento già selezionato per 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 soluzione della risposta selezionata non ha funzionato per me a partire dalla versione attuale di iOS v8.x. Ma @Andy offre una soluzione e io completerò:

    Sottoclassi UISegmentedControl e sovrascrivi il metodo touchesEnded nella sottoclass:

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

    Nella class in cui si intende utilizzare UISegmentedControl, creare un iVar currentSelectedSegIndex. Aggiungi UIControlEventAllTouchEvents accanto ai tuoi metodi di azione UIControlEventValueChanged:

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

    Implementa i due metodi di azione:

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