UISegmentedControl寄存器点击选定的段

时间:2009-10-25 13:51:19

标签: iphone uisegmentedcontrol

我有一个分段控件,用户可以在其中选择如何订购列表。工作正常。

但是,我希望在点击已选择的段时,订单会被反转。我已经准备好了所有代码,但我不知道如何在这些段上注册水龙头。似乎唯一可以使用的控制事件是UIControlEventValueChanged,但这不起作用(因为所选的段实际上并没有改变)。

有解决方案吗?如果是这样,它是什么?

提前致谢!

21 个答案:

答案 0 :(得分:51)

您可以继承UISegmentedControl,然后override setSelectedSegmentIndex:

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

如果使用IB,请确保将UISegmentedControl的类设置为子类。

现在您可以像平常一样听取UIControlEventValueChanged,除非用户取消选择细分,您会看到selectedSegmentIndex等于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]);
    }
}

答案 1 :(得分:31)

我认为如果你使用-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event,它会更好一些,因为这是UISegmentedControl的行为。此外,您似乎不需要重载-(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];
}

答案 2 :(得分:17)

自己想要这个。采取朱利安的解决方案(谢谢!)并略微修改。

以下UISegmentedControl子类只是触发UIControlEventValueChanged事件,即使值没有改变,这显然有点奇怪,但在我的情况下工作正常并保持简单。

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

答案 3 :(得分:12)

这适用于iOS 4和5:

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

答案 4 :(得分:11)

当前提供的解决方案仍然不起作用,因为除非真正触发新段,否则永远不会调用setSelectedSegmentIndex。至少在我的情况下这没用,我确实使用iOS5,也许这个解决方案在iOS4中运行。无论如何,这是我的解决方案。 它需要一个额外的子类方法,如下所示:

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

答案 5 :(得分:9)

这是一个非常古老的问题,所以我想我会添加在iOS 5+应用程序中遇到这个时使用的相当简单的解决方案。

我所做的只是将Tap Gesture Recognizer拖到我的.xib文件中的UISegmentedControl上。检查新手势识别器的连接,确保分段控件是唯一的gestureRecognizer插座,然后将其操作连接到您要在控制器中触发的方法。

此方法应在您触摸分段控件时触发,因此只需使用实例变量检查所选索引,以查看用户是否触摸了已选择的段。

@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

答案 6 :(得分:6)

在iOS8上,这里的每个答案似乎都不起作用或触发两次更改。我提出了非常简单的解决方案 - 所以我想分享它。

在子类中我只有:

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

它的作用是在更改分段控制段之前将所选段重置为-1。它将在touchesEnded方法中发生。

答案 7 :(得分:5)

这有效:

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

答案 8 :(得分:4)

雨燕5

class ReselectableSegmentedControl: UISegmentedControl {
    // Captures existing selected segment on touchesBegan.
    var oldValue: Int!

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        self.oldValue = self.selectedSegmentIndex
        super.touchesBegan(touches, with: event)
    }

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesEnded(touches, with: event)

        if self.oldValue == self.selectedSegmentIndex {
            self.sendActions(for: .valueChanged)
        }
    }
}

来自here

答案 9 :(得分:4)

Swift 3 中,我可以想象至少有两个解决方案如下。对于特殊情况,我还发布了第三个解决方案,其中所选分段用作切换按钮。

解决方案1:

提示:此解决方案仅适用于瞬时controlSegments!如果您需要固定控件的解决方案,请选择解决方案2

这个想法是注册第二个触发内部事件的事件:

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

解决方案2: 另一种方法是覆盖UISegmentedControl中的触摸功能并触发valueChanged,即使段索引未更改。因此,您可以按如下方式覆盖UISegmentedControl

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

解决方案3: 如果您的方法是将所选段作为切换按钮,则可以更改解决方案2以清理代码,如下所示:

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

现在我们可以按如下方式清理changeSelection函数:

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

答案 10 :(得分:2)

这是我的解决方案。如果你想在每次触摸时触发ValueChanged事件,我认为最优雅......

<强>·H

@interface UISegmentedControlAllChanges : UISegmentedControl
@end

<强>的.m

@implementation UISegmentedControlAllChanges

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

@end

答案 11 :(得分:1)

根据Bob de Graaf的回答:

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

请注意使用UIControlEventAllTouchEvents代替UIControlEventValueChanged

此外,无需拨打-(void)setSelectedSegmentIndex:

答案 12 :(得分:1)

在为我工作的代码段下面。重复点击同一段将取消选择它。

@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

答案 13 :(得分:1)

iOS 9解决方案。用这样的类重写UISegmentedControl:

@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

答案 14 :(得分:1)

大帮忙!我想要做的是设置一个或没有按钮的选项 - 当设置按钮时,第二次点击取消设置。这是我的修改:

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

首先通知让您在重置之前读取控件以查找当前设置。

答案 15 :(得分:1)

似乎是一个有趣的问题。该方案扩展了Steve E和yershuachu的解决方案。此版本使用UITapGestureRecognizer捕获所有触摸,并将selectedSegmentIndex设置为-1。但它也会将所有触摸传递给UISegmentedControl,因此它可以处理任何常规触摸。不需要子类。

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
}

答案 16 :(得分:1)

我的第一个想法是将Touch Up InsideTouch Down操作连接到您的排序方法,但这似乎不起作用。

第二个想法更多的是解决方法,在分段控件上设置Momentary属性。然后,每次点击时,都会触发Value Did Change操作。

答案 17 :(得分:0)

我正在使用KVO反转已经选择的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

答案 18 :(得分:0)

从当前iOS v8.x开始,所选答案的解决方案对我不起作用。但是@Andy提供了一个解决方案,我将完成:

子类化UISegmentedControl,并覆盖子类中的touchesEnded方法:

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

在您打算使用UISegmentedControl的类中,创建一个iVar currentSelectedSegIndex。在UIControlEventValueChanged操作方法旁边添加UIControlEventAllTouchEvents:

NSInteger currentSelectedSegIndex;

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

实施两种行动方法:

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

答案 19 :(得分:0)

这应该有效:

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

记住将此IBAction连接到 Connections Inspector 中的分段控制插座Value Changed

答案 20 :(得分:0)

由于UISegmentedControl负责设置细分,因此只能重置状态。我对其他人迅速5的建议做了一些修改:

//MARK: Custom segmentedControl
///  Every element works like a flipflop  [see](http://stackoverflow.com/questions/17652773/how-to-deselect-a-segment-in-segmented-control-button-permanently-till-its-click)
@IBDesignable class GRSegmentedControl: UISegmentedControl {
  private let url = Bundle.main.url(forResource: "PlopChoiceCntrl", withExtension: "aiff")!
  private var soundID:SystemSoundID = 0

  override init(items: [Any]?) {
    AudioServicesCreateSystemSoundID(url as CFURL, &soundID)
    super.init(items: items)
  }

  required init?(coder: NSCoder) {
    AudioServicesCreateSystemSoundID(url as CFURL, &soundID)
    super.init(coder: coder)
  }

  override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
    AudioServicesPlaySystemSound(soundID)
    let previousSelectedSegmentIndex = self.selectedSegmentIndex
    super.touchesEnded(touches, with: event)
    if previousSelectedSegmentIndex == self.selectedSegmentIndex {
      if let touch = touches.first{
        let touchLocation = touch.location(in: self)
        if bounds.contains(touchLocation) {
          self.selectedSegmentIndex = UISegmentedControl.noSegment
        }
      }
    }
  }
}