Регистр UISegmentedControl касается выбранного сегмента - PullRequest
61 голосов
/ 25 октября 2009

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

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

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

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

Ответы [ 19 ]

51 голосов
/ 03 апреля 2010

Вы можете подкласс 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]);
    }
}
31 голосов
/ 16 октября 2011

Я думаю, что даже немного лучше, если вы используете -(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];
}
17 голосов
/ 07 апреля 2010

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

Следующий подкласс 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
12 голосов
/ 11 декабря 2011

Это работает как на 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];
}
11 голосов
/ 14 октября 2011

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

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

Удачи!

9 голосов
/ 14 мая 2012

Это был довольно старый вопрос, поэтому я решил добавить довольно простое решение, которое я использовал, когда столкнулся с этим в приложении для 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
6 голосов
/ 05 мая 2016

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

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

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

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

5 голосов
/ 30 декабря 2011

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

- (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];
    }
}
4 голосов
/ 12 апреля 2017

В Свифт 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)")
    }
}
2 голосов
/ 30 апреля 2012

Вот мое решение. Я считаю самым элегантным, если вы хотите, чтобы событие 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
...