Регистр UISegmentedControl нажимает на выбранный сегмент

StackOverflow https://stackoverflow.com/questions/1620972

  •  06-07-2019
  •  | 
  •  

Вопрос

У меня есть сегментированный элемент управления, где пользователь может выбрать, как упорядочить список.Работает нормально.

Однако я бы хотел, чтобы при нажатии на уже выбранный сегмент порядок был инвертирован.У меня есть весь код на месте, но я не знаю, как зарегистрировать нажатия на эти сегменты.Кажется, единственное управляющее событие, которое вы можете использовать, - это UIControlEventValueChanged, но это не работает (поскольку выбранный сегмент фактически не меняется).

Есть ли решение для этого?И если да, то что это такое?

Заранее спасибо!

Это было полезно?

Решение

Вы можете создать подкласс UISegmentedControl , а затем переопределить 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]);
    }
}

Другие советы

Я думаю, что даже немного лучше, если вы используете - (void) touchesBegan: (NSSet *) touch с событиемEvent: (UIEvent *) --- так как это поведение 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];
}

Я сам этого хотел.Взял решение Джулиана (спасибо!) и немного изменил.

Следующий подкласс UISegmentedControl просто запускает UIControlEventValueChanged событие, даже если значение не изменилось, что, очевидно, выглядит немного странно, но в моем случае работает нормально и упрощает задачу.

АКСегментирОванный контроль.h

#import <UIKit/UIKit.h>

@interface AKSegmentedControl : UISegmentedControl {
}

@end

АКСегментирОванный контроль.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

Это работает как на 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];
}

Представленное текущее решение по-прежнему не работает, потому что setSelectedSegmentIndex никогда не вызывается, если только не будет затронут новый сегмент. По крайней мере, в моем случае это никогда не работало, хотя я использую iOS5, хотя, возможно, это решение работало в iOS4. Во всяком случае, это мое решение. Для этого требуется один дополнительный метод подкласса, а именно:

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

Удачи!

Это был довольно старый вопрос, поэтому я решил добавить довольно простое решение, которое я использовал, когда столкнулся с этим в приложении для iOS 5+.

Все, что я сделал, это перетащил Распознаватель жестов касания на UISegmentedControl в моем файле .xib. Проверьте соединения для вашего нового распознавателя жестов, чтобы убедиться, что сегментированный элемент управления является единственным выходом gestRecognizer, а затем просто подключите его действие к методу, который вы хотите запустить в своем контроллере.

Этот метод должен запускаться всякий раз, когда вы касаетесь сегментированного элемента управления, поэтому просто проверьте выбранный индекс, возможно, с помощью переменной экземпляра, чтобы убедиться, что пользователь коснулся уже выбранного сегмента.

@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

На iOS8 каждый ответ здесь, похоже, либо не работает, либо вызывает изменение дважды. Я придумал очень простое решение - поэтому я хотел бы поделиться им.

В подклассе у меня есть только:

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

Что нужно сделать, это сбросить выбранный сегмент на -1, прежде чем менять сегментированный контрольный сегмент. Это произойдет в методе touchesEnded.

Это работает:

- (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 я мог бы представить как минимум 2 решения следующим образом. Для особого случая я также опубликовал третье решение, в котором выбранный сегмент работает как кнопка переключения.

Решение 1:

Подсказка: это решение работает только с мгновенными управляющими сегментами! Если вам нужно решение для стационарного контроля, выберите Решение 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)")
    }
}

Вот мое решение.Самым элегантным, я думаю, будет, если вы хотите, чтобы событие valueChanged срабатывало при каждом касании...

@interface UISegmentedControlAllChanges : UISegmentedControl
@end

@implementation UISegmentedControlAllChanges

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

@end

Первой моей идеей было подключение действий Touch Up Inside или Touch Down к вашему методу сортировки, но, похоже, это не сработало.

Вторая идея - это скорее обходной путь, установите свойство Momentary в сегментированном элементе управления. Затем он будет запускать действие Значение изменилось при каждом его нажатии.

Большая помощь! То, что я хочу сделать, - это установить один или нет установленных кнопок - и когда кнопка установлена, второе нажатие отменяет ее. Это моя модификация:

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

Функция «Сначала уведомить» позволяет прочитать элемент управления, чтобы найти текущую настройку до ее сброса.

На основании ответа Боба де Граафа:

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

Обратите внимание на использование UIControlEventAllTouchEvents вместо UIControlEventValueChanged .

Также нет необходимости вызывать - (void) setSelectedSegmentIndex: .

Ниже приведен фрагмент кода, который работал на меня. Повторное нажатие на тот же сегмент отменяет его выбор.

@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. Переопределите 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

Похоже, это забавный вопрос. Эта схема распространяется на решения Стива Е и Йершуачу. Эта версия использует 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
}

Я использую 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

Решение с выбранным ответом не работало для меня с текущей версией iOS v8.x. Но @Andy предложит решение, и я закончу:

Создайте подкласс UISegmentedControl и перезапишите метод touchesEnded в подклассе:

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

В классе, где вы собираетесь использовать UISegmentedControl, создайте iVar currentSelectedSegIndex. Добавьте UIControlEventAllTouchEvents рядом с вашими методами действий UIControlEventValueChanged:

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
}

Это должно работать:

- (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 к вашей сегментированной контрольной розетке Value Changed в Инспекторе соединений .

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top