Как отменить выбор сегмента в Сегментированной кнопке управления до тех пор, пока он снова не будет нажат

Это случилось со мной. Если вы используете cocoapods, сделайте следующее:

  1. Удалить project.xcworkspace
  2. Переустановить модули с помощью «pod install» на терминале
  3. . Он создаст new project.xcworkspace
  4. Откройте новое project.xcworkspace -> Clean -> Build
29
задан icodebuster 15 July 2013 в 12:26
поделиться

8 ответов

Поскольку 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

Теперь вы получите 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;
    }
}

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 исправила проблему, замеченную Гжегожем.

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)
            }
        }
    }
}
84
ответ дан esilver 15 July 2013 в 12:26
поделиться

Вот исправление проблемы, которая возникает, когда вы пытаетесь отменить выделение, начиная с нажатия на 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];
    }
}
}
6
ответ дан Grzegorz Krukowski 15 July 2013 в 12:26
поделиться

В ответ на ответ @Matthias Bauch. Мне пришлось внести небольшие изменения в соответствии с Swift 2.2 в Xcode 7.3:

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)
                }
            }
        }
    }
}
1
ответ дан Kushal Ashok 15 July 2013 в 12:26
поделиться

Очень полезно! Спасибо! Я хотел немного больше контролировать события для своего проекта, поэтому я адаптировал ответ @ Matthias для отправки настраиваемого события «Значение без изменений». Я положил пример на GitHub .

Я также включил исправление @ Grzegorz, чтобы оно работало правильно, если пользователь тащит палец за сегментированный элемент управления.

1
ответ дан Geoff Hom 15 July 2013 в 12:26
поделиться

Вы можете сделать это со следующим (спасибо ответ Гжегожа и ответ Матиаса ):

- (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];
    }
}

Я сделал с открытым исходным кодом (MIT Лицензированный) класс STASegmentedControl (поддерживает iOS 7+), который имеет эту функциональность (и более).

2
ответ дан Community 15 July 2013 в 12:26
поделиться

Вот решение, которое не зависит от версии IOS. Это выбор самого поведения.

@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
1
ответ дан EisenRatte 15 July 2013 в 12:26
поделиться

swift 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)
                }
            }
        }
    }
}
1
ответ дан Talles Borges 15 July 2013 в 12:26
поделиться

Что касается @Stunner, это мой вклад в достижение цели. Я что-то изменил и добавил свойство _previousSelectedSegmentIndex; в коде @Stunner переменная previousSelectedSegmentIndex оказалась бесполезной:

@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;
}
0
ответ дан Gio Cat 15 July 2013 в 12:26
поделиться