如何在分段控制button中永久取消select一个段,直到再次点击

我有一个UISegmentedControl 4段。 当它被选中时,它应该保持selected state 。 当再次点击同一个段时,应该deselect itself 。 如何做到这一点?

由于UISegmentedControl仅在未select段的情况下才会发送操作,所以必须对UISegmentedControl进行子类化,以便对触摸操作进行细微的更改。 我使用这个类:

 @implementation MBSegmentedControl // this sends a value changed event even if we reselect the currently selected segment - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { NSInteger current = self.selectedSegmentIndex; [super touchesBegan:touches withEvent:event]; if (current == self.selectedSegmentIndex) { [self sendActionsForControlEvents:UIControlEventValueChanged]; } } @end 

现在,即使段已被选中,您也将获得UIControlEventValueChanged事件。 只需将当前索引保存在一个variables中,并在动作中进行比较。 如果两个索引匹配,则必须取消select触摸的段。

 // _selectedSegmentIndex is an instance variable of the view controller - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; _selectedSegmentIndex = self.segment.selectedSegmentIndex; } - (IBAction)segmentChanged:(UISegmentedControl *)sender { if (sender.selectedSegmentIndex == _selectedSegmentIndex) { NSLog(@"Segment %d deselected", sender.selectedSegmentIndex); sender.selectedSegmentIndex = UISegmentedControlNoSegment; _selectedSegmentIndex = UISegmentedControlNoSegment; } else { NSLog(@"Segment %d selected", sender.selectedSegmentIndex); _selectedSegmentIndex = sender.selectedSegmentIndex; } } 

iOS 7改变了如何处理UISegmentedControl的触摸。 selectedSegmentIndex现在在touchesEnded:更改。

所以更新的子类应该是这样的:

 @implementation MBSegmentedControl + (BOOL)isIOS7 { static BOOL isIOS7 = NO; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ NSInteger deviceSystemMajorVersion = [[[[[UIDevice currentDevice] systemVersion] componentsSeparatedByString:@"."] objectAtIndex:0] integerValue]; if (deviceSystemMajorVersion >= 7) { isIOS7 = YES; } else { isIOS7 = NO; } }); return isIOS7; } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { NSInteger previousSelectedSegmentIndex = self.selectedSegmentIndex; [super touchesBegan:touches withEvent:event]; if (![[self class] isIOS7]) { // before iOS7 the segment is selected in touchesBegan if (previousSelectedSegmentIndex == self.selectedSegmentIndex) { // if the selectedSegmentIndex before the selection process is equal to the selectedSegmentIndex // after the selection process the superclass won't send a UIControlEventValueChanged event. // So we have to do this ourselves. [self sendActionsForControlEvents:UIControlEventValueChanged]; } } } - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { NSInteger previousSelectedSegmentIndex = self.selectedSegmentIndex; [super touchesEnded:touches withEvent:event]; if ([[self class] isIOS7]) { // on iOS7 the segment is selected in touchesEnded if (previousSelectedSegmentIndex == self.selectedSegmentIndex) { [self sendActionsForControlEvents:UIControlEventValueChanged]; } } } @end 

Swift 2.2版本,解决了Grzegorz注意到的问题。

 class ReselectableSegmentedControl: UISegmentedControl { @IBInspectable var allowReselection: Bool = true override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) { let previousSelectedSegmentIndex = self.selectedSegmentIndex super.touchesEnded(touches, withEvent: event) if allowReselection && previousSelectedSegmentIndex == self.selectedSegmentIndex { if let touch = touches.first { let touchLocation = touch.locationInView(self) if CGRectContainsPoint(bounds, touchLocation) { self.sendActionsForControlEvents(.ValueChanged) } } } } } 

Swift 3.0将此修正更改为如下所示:

 class MyDeselectableSegmentedControl: UISegmentedControl { override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { let previousIndex = selectedSegmentIndex super.touchesEnded(touches, with: event) if previousIndex == selectedSegmentIndex { let touchLocation = touches.first!.location(in: self) if bounds.contains(touchLocation) { sendActions(for: .valueChanged) } } } } 

这里是一个问题的解决办法,当你尝试通过开始点击UISegmentControl取消select,然后你完成外部触摸 – 它仍然取消select。

 - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { CGPoint locationPoint = [[touches anyObject] locationInView:self]; CGPoint viewPoint = [self convertPoint:locationPoint fromView:self]; if ([self pointInside:viewPoint withEvent:event]) { int oldValue = self.selectedSegmentIndex; [super touchesEnded:touches withEvent:event]; if (oldValue == self.selectedSegmentIndex) { [super setSelectedSegmentIndex:UISegmentedControlNoSegment]; [self sendActionsForControlEvents:UIControlEventValueChanged]; } } } 

你可以这样做(感谢Grzegorz的回答和Matthias的回答 ):

 - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { NSInteger previousSelectedSegmentIndex = self.selectedSegmentIndex; [super touchesEnded:touches withEvent:event]; CGPoint locationPoint = [[touches anyObject] locationInView:self]; CGPoint viewPoint = [self convertPoint:locationPoint fromView:self]; if ([self pointInside:viewPoint withEvent:event] && previousSelectedSegmentIndex == self.selectedSegmentIndex) { self.selectedSegmentIndex = UISegmentedControlNoSegment; [self sendActionsForControlEvents:UIControlEventValueChanged]; } } 

我已经开发了一个开放源代码(麻省理工学院许可)类STASegmentedControl (支持iOS 7+),它具有此function(以及更多)。

很有帮助! 谢谢! 我想要更多的控制我的项目的事件,所以我改编@ Matthias的答案发送一个自定义的“价值不变”事件。 我在GitHub上做了一个例子。

我还join了@ Grzegorz的修复,所以如果用户在分段控制之外拖动她的手指,它的行为是正确的。

这是一个独立于IOS版本的解决scheme。 这是select行为本身。

 @interface CustomSegmentedControl : UISegmentedControl @end @implementation CustomSegmentedControl{ BOOL _touchBegan; BOOL _reactOnTouchBegan; } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { _touchBegan = YES; NSInteger previousSelectedSegmentIndex = self.selectedSegmentIndex; [super touchesBegan:touches withEvent:event]; if (_reactOnTouchBegan) { // before iOS7 the segment is selected in touchesBegan if (previousSelectedSegmentIndex == self.selectedSegmentIndex) { [self sendActionsForControlEvents:UIControlEventValueChanged]; } } } -(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { _touchBegan = NO; NSInteger previousSelectedSegmentIndex = self.selectedSegmentIndex; [super touchesEnded:touches withEvent:event]; if (!_reactOnTouchBegan) { CGPoint locationPoint = [[touches anyObject] locationInView:self]; CGPoint viewPoint = [self convertPoint:locationPoint fromView:self]; if ([self pointInside:viewPoint withEvent:event]) { // on iOS7 the segment is selected in touchesEnded if (previousSelectedSegmentIndex == self.selectedSegmentIndex) { [self sendActionsForControlEvents:UIControlEventValueChanged]; } } } } - (void)sendActionsForControlEvents:(UIControlEvents)controlEvents { if(controlEvents == UIControlEventValueChanged){ _reactOnTouchBegan = _touchBegan; } [super sendActionsForControlEvents:controlEvents]; } @end 

参考@Matthias Bauch发表的答案。 我不得不根据Xcode 7.3中的Swift 2.2做一些改变:

 class ReselectableSegmentedControl: UISegmentedControl { @IBInspectable var allowReselection: Bool = true override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) { let previousSelectedSegmentIndex = self.selectedSegmentIndex super.touchesEnded(touches, withEvent: event) if allowReselection && previousSelectedSegmentIndex == self.selectedSegmentIndex { if let touch = touches.first { let touchLocation = touch.locationInView(self) if CGRectContainsPoint(bounds, touchLocation) { self.sendActionsForControlEvents(.ValueChanged) } } } } } 

快速3.1版本发布@ Kushal Ashok

 class ReselectableSegmentedControl: UISegmentedControl { @IBInspectable var allowReselection: Bool = true override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { let previousSelectedSegmentIndex = self.selectedSegmentIndex super.touchesEnded(touches, with: event) if allowReselection && previousSelectedSegmentIndex == self.selectedSegmentIndex { if let touch = touches.first { let touchLocation = touch.location(in: self) if bounds.contains(touchLocation) { self.sendActions(for: .valueChanged) } } } } } 

提到@Stunner,这是我实现这个目标的贡献。 我改变了一些东西,并添加了属性_previousSelectedSegmentIndex; 在@Stunner代码中,variablespreviousSelectedSegmentIndex是无用的:

 @implementation STASegmentedControl { NSInteger _previousSelectedSegmentIndex; } - (void)setSelectedSegmentIndex:(NSInteger)selectedSegmentIndex { [super setSelectedSegmentIndex: selectedSegmentIndex]; _previousSelectedSegmentIndex = self.selectedSegmentIndex; } - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { [super touchesEnded:touches withEvent:event]; CGPoint locationPoint = [[touches anyObject] locationInView:self]; CGPoint viewPoint = [self convertPoint:locationPoint fromView:self]; if (self.toggleableSegments) { // toggle selected segment on/off if ([self pointInside:viewPoint withEvent:event] && _previousSelectedSegmentIndex == self.selectedSegmentIndex) { self.selectedSegmentIndex = UISegmentedControlNoSegment; [self sendActionsForControlEvents:UIControlEventValueChanged]; } } _previousSelectedSegmentIndex = self.selectedSegmentIndex; }