IT TIP

UISegmentedControl 레지스터는 선택한 세그먼트를 탭합니다.

itqueen 2020. 11. 29. 12:47
반응형

UISegmentedControl 레지스터는 선택한 세그먼트를 탭합니다.


사용자가 목록을 주문하는 방법을 선택할 수있는 세그먼트 화 된 컨트롤이 있습니다. 잘 작동합니다.

그러나 이미 선택한 세그먼트를 탭하면 순서가 반전되기를 바랍니다. 모든 코드가 제자리에 있지만 해당 세그먼트에 탭을 등록하는 방법을 모르겠습니다. 사용할 수있는 유일한 제어 이벤트는 UIControlEventValueChanged 인 것 같지만 작동하지 않습니다 (선택한 세그먼트가 실제로 변경되지 않기 때문에).

이에 대한 해결책이 있습니까? 그렇다면 그것은 무엇입니까?

미리 감사드립니다!


하위 클래스 UISegmentedControl를 만든 다음override setSelectedSegmentIndex:

- (void) setSelectedSegmentIndex:(NSInteger)toValue {
    if (self.selectedSegmentIndex == toValue) {
        [super setSelectedSegmentIndex:UISegmentedControlNoSegment];
    } else {
        [super setSelectedSegmentIndex:toValue];        
    }
}

IB를 사용하는 경우 클래스를 UISegmentedControl하위 클래스로 설정했는지 확인하십시오 .

이제 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 *)eventUISegmentedControl의 동작이므로 --- 사용하면 조금 더 좋습니다 . 또한 과부하가 필요하지 않은 것 같습니다.-(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];
}

이것을 직접 원했습니다. Julian의 해결책을 취하고 (감사합니다!) 약간 수정되었습니다.

다음 UISegmentedControl 하위 클래스 UIControlEventValueChanged는 값이 변경되지 않은 경우에도 이벤트 를 트리거합니다 . 이는 분명히 약간 이상하게 읽지 만 제 경우에는 잘 작동하고 일을 간단하게 유지합니다.

AKSegmentedControl.h

#import <UIKit/UIKit.h>

@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에서 작동했을 것입니다. 어쨌든 이것은 내 해결책입니다. 다음과 같은 추가 하위 클래스 메서드가 하나 필요합니다.

-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{    
    [self setSelectedSegmentIndex:self.selectedSegmentIndex];
    [super touchesEnded:touches withEvent:event];
}

행운을 빕니다!


이것은 꽤 오래된 질문이어서 iOS 5+ 앱에서이 문제를 접했을 때 사용한 다소 간단한 솔루션을 추가 할 것이라고 생각했습니다.

내가 한 것은 탭 제스처 인식기를 .xib 파일의 UISegmentedControl로 드래그하는 것뿐이었습니다. 새 제스처 인식기의 연결을 확인하여 세그먼트 화 된 컨트롤이 유일한 gestureRecognizer 콘센트인지 확인한 다음 해당 동작을 컨트롤러에서 실행하려는 메서드에 연결하면됩니다.

이 메서드는 세그먼트 화 된 컨트롤을 터치 할 때마다 실행되어야하므로 인스턴스 변수를 사용하여 선택한 인덱스를 확인하여 사용자가 이미 선택한 세그먼트를 터치했는지 확인합니다.

@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에서 모든 답변은 작동하지 않거나 변경을 두 번 트리거하는 것 같습니다. 저는 아주 간단한 해결책을 생각해 냈습니다. 그래서 그것을 공유하고 싶습니다.

하위 클래스에서는 다음 만 있습니다.

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    [super touchesBegan:touches withEvent:event];
    [self setSelectedSegmentIndex:UISegmentedControlNoSegment];
}

그것이하는 일은 세그먼트 화 된 제어 세그먼트를 변경하기 전에 선택된 세그먼트를 -1로 재설정하는 것입니다. touchesEnded 메서드에서 발생합니다.


이것은 작동합니다 :

- (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 에서는 다음과 같이 적어도 두 가지 솔루션을 상상할 수 있습니다. 특별한 경우를 위해 선택한 세그먼트가 토글 버튼으로 작동하는 세 번째 솔루션도 게시했습니다.

해결책 1 :

힌트 :이 솔루션은 순간적인 controlSegments에서만 작동합니다! 고정 제어에 대한 솔루션이 필요하면 솔루션 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<UITouch>, with event: UIEvent?) {
        current = self.selectedSegmentIndex
        super.touchesBegan(touches, with: event)
    }
    override func touchesEnded(_ touches: Set<UITouch>, 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<UITouch>, with event: UIEvent?) {
        current = self.selectedSegmentIndex
        super.touchesBegan(touches, with: event)
    }
    override func touchesEnded(_ touches: Set<UITouch>, 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)")
    }
}

여기 내 해결책이 있습니다. ValueChanged 이벤트가 모든 터치에서 발생하기를 원한다면 가장 우아한 것입니다 ...

.h

@interface UISegmentedControlAllChanges : UISegmentedControl
@end

.미디엄

@implementation UISegmentedControlAllChanges

-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{    
    [self sendActionsForControlEvents:UIControlEventValueChanged];
    [super touchesEnded:touches withEvent:event];
}

@end

내가 가진 첫 번째 아이디어는 Touch Up Inside또는 Touch Down작업을 정렬 방법에 연결하는 것이었지만 이것은 작동하지 않는 것 같습니다.

두 번째 아이디어는 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]; 
  }
}

먼저 알림을 사용하면 컨트롤을 읽고 재설정하기 전에 현재 설정을 찾을 수 있습니다.


Bob de Graaf의 답변을 바탕으로 :

-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self sendActionsForControlEvents:UIControlEventAllTouchEvents];
    [super touchesEnded:touches withEvent:event];
}

사용 주 UIControlEventAllTouchEvents대신을 UIControlEventValueChanged.

또한를 호출 할 필요가 없습니다 -(void)setSelectedSegmentIndex:.


나를 위해 일한 코드 아래. 같은 세그먼트를 반복해서 탭하면 선택이 해제됩니다.

@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을 다음 클래스로 재정의합니다.

@interface SegmentedControl()

@property (nonatomic) NSInteger previousCurrent;

@end

@implementation SegmentedControl

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    self.previousCurrent = self.selectedSegmentIndex;
    [super touchesBegan:touches withEvent:event];
}

- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    [super touchesEnded:touches withEvent:event];

    if (self.selectedSegmentIndex == self.previousCurrent) {
        [self sendActionsForControlEvents:UIControlEventValueChanged];
    }

    self.previousCurrent = NSNotFound;
}

@end

대답하기에 재미있는 질문처럼 보입니다. 이 계획은 Steve E와 yershuachu의 솔루션을 확장합니다. 이 버전은 UITapGestureRecognizer를 사용하여 모든 터치를 캡처하고 selectedSegmentIndex를 -1로 설정합니다. 그러나 모든 터치를 UISegmentedControl에 전달하므로 모든 일반 터치를 처리 할 수 ​​있습니다. 하위 분류가 필요하지 않습니다.

UISegmentedControl *mySegControl;

- (void)viewDidLoad
{
    [super viewDidLoad];

    [mySegControl addTarget:self action:@selector(segmentAction:)
           forControlEvents:UIControlEventValueChanged];
    // allow the a seg button tap to be seen even if already selected
    UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc]
                                          initWithTarget:self action:@selector(unitsSegTap:)];
    tapGesture.cancelsTouchesInView = NO; // pass touches through for normal taps
    [mySegControl addGestureRecognizer:tapGesture];

    // continue setup ...
}

- (void)segTap:(UITapGestureRecognizer *)sender
{
    mySegControl.selectedSegmentIndex = -1;
}

// called for UIControlEventValueChanged
- (void)segmentAction:(UISegmentedControl *)sender
{
    NSInteger index = sender.selectedSegmentIndex;
    NSLog(@"unitsSegmentAction %d",(int)index);
    // process the segment
}

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는 솔루션을 제공하고 완료합니다.

하위 클래스에서 UISegmentedControl, 및 덮어 쓰기 touchesEnded메서드를 하위 클래스에서 :

-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self sendActionsForControlEvents:UIControlEventAllTouchEvents];
    [super touchesEnded:touches withEvent:event];
}

UISegmentedControl을 사용하려는 클래스에서 iVar currentSelectedSegIndex를 만듭니다. UIControlEventValueChanged 작업 메서드 옆에 UIControlEventAllTouchEvents를 추가합니다.

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
}

참고URL : https://stackoverflow.com/questions/1620972/uisegmentedcontrol-register-taps-on-selected-segment

반응형