views:

1565

answers:

4

Hi,

I have a segmented control where the user can select how to order a list. Works fine.

However, I would like that when an already selected segment is tapped, the order gets inverted. I have all the code in place, but I don't know how to register the taps on those segments. It seems the only control event you can use is UIControlEventValueChanged, but that isn't working (since the selected segment isn't actually changing).

Is there a solution for this? And if so, what is it?

Thanks in advance!

A: 

The first idea I had was wire up the Touch Up Inside or Touch Down actions to your sort method, but this doesn't seem to work.

The second idea is more of a work around, set the Momentary property on the segmented control. This will then fire a Value Did Change action every time it is tapped.

rjstelling
You'd get notified of all touches, but a `momentary` control doesn't show any segment as currently selected, which might be kind of confusing.
Sixten Otto
Your first idea was mine as well... And as Sixten points out, your second idea is not really ideal.
Jongsma
You might want to ask another question on why UISegmentedControl doesn't receive Touch Up Inside actions.
rjstelling
+3  A: 

You can subclass UISegmentedControl, and then override setSelectedSegmentIndex:

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

If using IB, make sure you set the class of your UISegmentedControl to your subclass.

Now you can listen for the same UIControlEventValueChanged as you would normally, except if the user deselected the segment, you will see a selectedSegmentIndex equal to 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]);
}}
Julian
+2  A: 

Wanted this myself. Took Julian's solution (thanks!) and modified slightly.

The following UISegmentedControl subclass simply triggers the UIControlEventValueChanged event even when the value didn't change, which obviously reads a bit weird, but works fine in my case and keeps things simple.

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
Henrik N
This seems to be the trick I was looking for - I have a segmented control with one segment that I need to trigger a modal view to appear whenever it's touched. Thanks.
petert
For a single segmented control (as petert mentioned), also see http://gist.github.com/401670.
Henrik N
A: 

Big help! What I want to do is have the option of one or no buttons set - and when a button is set, a second tap unsets it. This is my modification:

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

Notify first lets you read the control to find the current setting before it's reset.

David H
It might be related, but this is another question.
petert
UPDATE - sorry, misread the start of the answer. Would've been nice if source code was formatted, by indenting each line by 4 spaces.
petert