I have a segmented control where the user can select how to order a list. Works fine.
However, I would like that when an already selected segment is tapped, the orde
Because the UISegmentedControl is in charge of setting the segment, it should only reset the state. I modify a little the suggestions of other guys for swift 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
}
}
}
}
}
This works:
- (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];
}
}
Below the piece of code that did work for me. Repeated tap on the same segment will deselect it.
@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
The current solution presented still does not work, because setSelectedSegmentIndex is never called unless really a new segment is tapped. At least in my case this never worked, I do use iOS5 though, perhaps this solution did work in iOS4. Anyway, this is my solution. It needs one extra subclass method, which is the following:
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[self setSelectedSegmentIndex:self.selectedSegmentIndex];
[super touchesEnded:touches withEvent:event];
}
Good luck!
Swift 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)
}
}
}
From here
I'm using KVO to invert already selected segment for 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