如何永久取消选择分段控制按钮中的段,直到再次单击它

Pra*_*mar 30 objective-c uibutton uisegmentedcontrol ios

我有一个UISegmentedControl4段.当它被选中时,它应该保持selected state.当再次单击相同的段时,它应该deselect itself.怎么做到这一点?

Mat*_*uch 85

由于UISegmentedControl仅在选择了未选择的段时才发送操作,因此必须进行子类化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
Run Code Online (Sandbox Code Playgroud)

现在UIControlEventValueChanged即使已经选择了段,您也将获得事件.只需将当前索引保存在变量中并在操作中进行比较即可.如果两个索引匹配,则必须取消选择触摸的段.

// _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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

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)
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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)
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 您还可以通过在`[self sendActionsForControlEvents:UIControlEventValueChanged]之前添加`self.selectedSegmentIndex = UISegmentedControlNoSegment;`来封装此子类中的取消选择代码;` (6认同)

Grz*_*ski 6

这是一个问题的修复,当你尝试通过开始点击UISegmentControl取消选择,然后你完成外面的触摸 - 它仍然取消选择.

- (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];
    }
}
}
Run Code Online (Sandbox Code Playgroud)