UISegmentedControl регистрирует краны на выбранном сегменте
У меня есть сегментированный элемент управления, где пользователь может выбрать способ заказа списка. Работает отлично.
Тем не менее, мне бы хотелось, чтобы при отборе уже выбранного сегмента порядок был инвертирован. У меня есть весь код на месте, но я не знаю, как регистрировать краны на этих сегментах. Кажется, единственным управляющим событием, которое вы можете использовать, является UIControlEventValueChanged, но это не работает (поскольку выбранный сегмент фактически не меняется).
Есть ли решение для этого? И если да, то что это?
- Применить форматирование валюты к UITextField в событии изменения
- Метод доступа после завершения анимации UIImageView
- Как добавить userInfo в UIAlertView?
- iPhone: Как получить текущие миллисекунды?
- Как настроить таргетинг на определенную версию iPhone?
Заранее спасибо!
- Как подключить / отключить Bluetooth и привязку к Snow Leopard
- Разработка iPhone: как создать цветной или полупрозрачный фон для UIActionSheet?
- Воспроизведение аудио iPhone: сила через внутренний динамик?
- Objective-c iPhone-процент кодирует строку?
- Как строка состояния «В вызове» влияет на размер представления UIViewController? (и как правильно обращаться с ним)
- Как продлить сертификат разработки iPhone?
- Остановить UIWebView от «подпрыгивания» по вертикали?
- @property @synthesize
Вы можете подclassифицировать UISegmentedControl, а затем переопределить setSelectedSegmentIndex:
- (void) setSelectedSegmentIndex:(NSInteger)toValue { if (self.selectedSegmentIndex == toValue) { [super setSelectedSegmentIndex:UISegmentedControlNoSegment]; } else { [super setSelectedSegmentIndex:toValue]; } }
Если вы используете IB, убедитесь, что вы установили class вашего UISegmentedControl в свой подclass.
Теперь вы можете прослушивать тот же 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 *)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]; }
Хотел это сам. Взял решение Джулиана (спасибо!) И немного изменил.
Следующий подclass UISegmentedControl просто запускает событие UIControlEventValueChanged
даже когда значение не изменилось, что, очевидно, UIControlEventValueChanged
немного странно, но отлично работает в моем случае и сохраняет все просто.
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
Это работает как на 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. Во всяком случае, это мое решение. Ему нужен один дополнительный метод подclassа, который выглядит следующим образом:
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { [self setSelectedSegmentIndex:self.selectedSegmentIndex]; [super touchesEnded:touches withEvent:event]; }
Удачи!
Это был довольно старый вопрос, поэтому я подумал, что добавлю довольно простое решение, которое я использовал, когда столкнулся с этим в приложении iOS 5+.
Все, что я сделал, это перетащить Recognizer Tap Gesture на UISegmentedControl в моем .xib-файле. Проверьте соединения для вашего нового распознавателя жестов, чтобы убедиться, что сегментированное управление является единственным выходом gestureRecognizer, а затем просто подключите его к методу, который вы хотите запустить в своем controllerе.
Этот метод должен срабатывать в любое время, когда вы касаетесь сегментированного элемента управления, поэтому просто проверяйте выбранный индекс с помощью, возможно, переменной экземпляра, чтобы увидеть, коснулся ли пользователь уже выбранного сегмента.
@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 каждый ответ здесь кажется либо нерабочим, либо триггерным изменением дважды. Я придумал очень простое решение – поэтому я хотел бы поделиться им.
В подclassе у меня есть только:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { [super touchesBegan:touches withEvent:event]; [self setSelectedSegmentIndex:UISegmentedControlNoSegment]; }
Это делается для сброса выбранного сегмента на -1 перед сменой сегментированного сегмента управления. Это произойдет с помощью метода toucedEnded.
Это работает:
- (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, 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") } } }
Решение 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, 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 } } }
Теперь мы можем очистить функцию changeSelection
следующим образом:
func changeSelection(sender: UISegmentedControl) { switch sender.selectedSegmentIndex { case UISegmentedControlNoSegment: print("none") default: print("selected: \(sender.selectedSegmentIndex)") } }
дляfunc changeSelection(sender: UISegmentedControl) { switch sender.selectedSegmentIndex { case UISegmentedControlNoSegment: print("none") default: print("selected: \(sender.selectedSegmentIndex)") } }
Вот мое решение. Самый элегантный, я думаю, если вы хотите, чтобы событие ValueChanged срабатывало при каждом касании …
.час
@interface UISegmentedControlAllChanges : UISegmentedControl @end
.m
@implementation UISegmentedControlAllChanges -(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { [self sendActionsForControlEvents:UIControlEventValueChanged]; [super touchesEnded:touches withEvent:event]; } @end
Первой идеей, с которой я столкнулся, было подключение методов Touch Up Inside
или Touch Down
к вашему методу сортировки, но это, похоже, не работает.
Вторая идея – это скорее работа, Momentary
свойство Momentary
на сегментированном элементе управления. Затем он будет запускать действие Value Did Change
каждом нажатии.
Большая помощь! То, что я хочу сделать, – это вариант с одной или отсутствующими кнопками – и когда установлена кнопка, второй сигнал отключает ее. Это моя модификация:
- (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:
Ниже fragmentа кода, который работал для меня. Повторное нажатие на том же сегменте отменит его выбор.
@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 с таким 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
Я использую 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 предлагает решение, и я завершу:
Подclass UISegmentedControl
и перезаписать метод touchesEnded
в подclassе:
-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { [self sendActionsForControlEvents:UIControlEventAllTouchEvents]; [super touchesEnded:touches withEvent:event]; }
В classе, где вы собираетесь использовать 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 }