UIPicker обнаруживает нажатие на текущую выбранную строку

У меня есть UIPickerView и метод didSelectRow не вызывается при нажатии на выбранную строку. Мне нужно разобраться с этим делом. Любые идеи?


person Royce    schedule 18.08.2010    source источник
comment
Честно говоря, слегка удивлен, что никто не знает, что здесь делать. Одна вещь, о которой я могу думать, это то, что вы можете подклассировать представление выбора и добавить свои собственные методы касания, но это кажется излишним и грязным. didSelectRow вызывается все время, когда строка становится выбранной строкой, но не тогда, когда пользователь нажимает (выбирает) строку, которая уже выбрана. Должен быть метод, к которому я могу подключиться, чтобы поймать этот случай без подкласса...   -  person Royce    schedule 20.08.2010
comment
аналогичный вопрос: stackoverflow .com/questions/567805/   -  person andy boot    schedule 05.06.2011


Ответы (8)


Во-первых, согласуйте класс с протоколом UIGestureRecognizerDelegate.

Затем в настройках просмотра:

UITapGestureRecognizer *tapToSelect = [[UITapGestureRecognizer alloc]initWithTarget:self
                                                                                 action:@selector(tappedToSelectRow:)];
tapToSelect.delegate = self;
[self.pickerView addGestureRecognizer:tapToSelect];

И в другом месте:

#pragma mark - Actions

- (IBAction)tappedToSelectRow:(UITapGestureRecognizer *)tapRecognizer
{
    if (tapRecognizer.state == UIGestureRecognizerStateEnded) {
        CGFloat rowHeight = [self.pickerView rowSizeForComponent:0].height;
        CGRect selectedRowFrame = CGRectInset(self.pickerView.bounds, 0.0, (CGRectGetHeight(self.pickerView.frame) - rowHeight) / 2.0 );
        BOOL userTappedOnSelectedRow = (CGRectContainsPoint(selectedRowFrame, [tapRecognizer locationInView:self.pickerView]));
        if (userTappedOnSelectedRow) {
            NSInteger selectedRow = [self.pickerView selectedRowInComponent:0];
            [self pickerView:self.pickerView didSelectRow:selectedRow inComponent:0];
        }
    }
}

#pragma mark - UIGestureRecognizerDelegate

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
    return true;
}
person Nikolay Shubenkov    schedule 08.09.2014
comment
Чтобы этот код работал, я добавил -(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneousWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{ // return return true; } как указано здесь: stackoverflow.com/questions/22319427/ - person rsc; 17.02.2015
comment
Этот код также сработает, если вы прокрутите микрометр, а затем решите остаться на том же объекте. Чтобы решить эту проблему, я добавил распознаватель жестов панорамирования, чтобы определить, прокручивал ли пользователь. В состоянии прокрутки я ничего не делал в обратном вызове распознавателя касаний. - person Adam Johns; 09.12.2016
comment
@AdamJohns Я пытался добавить PanGesture в свой инструмент выбора. Но селектор PanGesture не вызывает. У меня также есть UITapGestureRecognizer с этим представлением выбора. Есть ли у вас какие-либо идеи или предложения для этого? - person AnujAroshA; 29.08.2017

Ответ Николая в Swift 4:

Во-первых, добавьте UITapGestureRecognizer к вашему UIPickerView в viewDidLoad() и пусть ваш UIViewController соответствует UIGestureRecognizerDelegate.

let tap = UITapGestureRecognizer(target: self, action: #selector(pickerTapped))
tap.delegate = self
self.pickerView.addGestureRecognizer(tap)

Добавьте эту функцию, которая вызывает ваш UIPickerViewDelegate при обнаружении нажатия на строку:

@objc func pickerTapped(tapRecognizer: UITapGestureRecognizer) {
    if tapRecognizer.state == .ended {
        let rowHeight = self.pickerView.rowSize(forComponent: 0).height
        let selectedRowFrame = self.pickerView.bounds.insetBy(dx: 0, dy: (self.pickerView.frame.height - rowHeight) / 2)
        let userTappedOnSelectedRow = selectedRowFrame.contains(tapRecognizer.location(in: self.pickerView))
        if userTappedOnSelectedRow {
            let selectedRow = self.pickerView.selectedRow(inComponent: 0)
            pickerView(self.pickerView, didSelectRow: selectedRow, inComponent: 0)
        }
    }
}

Добавьте метод shouldRecognizeSimultaneouslyWith из UIGestureRecognizerDelegate:

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
    return true
}
person ElegyD    schedule 07.02.2018
comment
Вместо того, чтобы писать return true внутри funcgestureRecognizer(...shouldRecognizeSimultaneousWith otherGestureRecognizer...), вы можете сначала проверить представление чем-то вроде: Это необходимо, если вы используете жестRecognizer в других представлениях в ViewController. - person iOS_Mouse; 11.12.2020

Ответ Николая с Swift:

let tap = UITapGestureRecognizer(target: self, action: #selector(OCAccountSettingsViewController.pickerTapped(_:)))
tap.cancelsTouchesInView = false
tap.delegate = self
pickerView.addGestureRecognizer(tap)

важно, пусть ваш viewController подтвердит UIGestureRecognizerDelegate, иначе обработчик не сработает:

func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true}

последним шагом будет обработчик события tap:

func pickerTapped(tapRecognizer:UITapGestureRecognizer)
{
    if (tapRecognizer.state == UIGestureRecognizerState.Ended)
    {
      let rowHeight : CGFloat  = self.pickerView.rowSizeForComponent(0).height
      let selectedRowFrame: CGRect = CGRectInset(self.pickerView.bounds, 0.0, (CGRectGetHeight(self.pickerView.frame) - rowHeight) / 2.0 )
      let userTappedOnSelectedRow = (CGRectContainsPoint(selectedRowFrame, tapRecognizer.locationInView(pickerView)))
      if (userTappedOnSelectedRow)
      {
        let selectedRow = self.pickerView.selectedRowInComponent(0)
        //do whatever you want here
      }
    }
  }
person Dania Delbani    schedule 02.01.2017

Решение touchesBegan: / touchesEnded: у меня отлично работало при использовании iOS 4.2/4.3, но они перестали работать с iOS. Наконец, я получил это решение, которое может быть полезным: использование распознавания жестов касания.

    IBOutlet UIPickerView *picker;

    [picker addGestureRecognizer:
        [[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(pickerTapped:)] autorelease]
    ];

В этом случае, когда пользователь нажимает на окно выбора, селектор

    -(void)pickerTapped:(UIGestureRecognizer *)gestureRecognizer

вызывается. У меня работало на обеих iOS 4.2+ / 5.0

person Alex Barinov    schedule 24.01.2012

Вот быстрый код для выяснения, нажал ли пользователь внутри выбранной строки или нет. Я только что преобразовал код obj-c из ответа Николая.

func pickerTapped(sender: UITapGestureRecognizer){
    let rowHeight = self.rowSizeForComponent(0).height
    let selectedRowFrame = CGRectInset(self.bounds, 0.0, (CGRectGetHeight(self.frame) - rowHeight) / 2.0)
    let userTappedOnSelectedRow = CGRectContainsPoint(selectedRowFrame, sender.locationInView(self))
    if( userTappedOnSelectedRow ){
        // .. your action here ..
    }
}
person Joosep Simm    schedule 14.08.2015

Я решил эту проблему, создав подкласс общего супервизора и перехватив события касания, прежде чем отправлять их вперед. В построителе интерфейсов я также добавил кнопку там, где находится область выбора, прикрепил действие к событию касания и отправил кнопку «назад» (это очень важно, чтобы она не возвращалась в hitTest). Самый актуальный код ниже. Это может быть улучшено для более сложных случаев, таких как мультитач.

    @implementation InterceptorView


-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
 UITouch *t=[touches anyObject];
 CGPoint p=[t locationInView:button];
 if ([button hitTest:p withEvent:event]) 
  started_in_button=YES;
 [hitView touchesBegan:touches withEvent:event];
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
 [hitView touchesMoved:touches withEvent:event];
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
 UITouch *t=[touches anyObject];
 CGPoint p=[t locationInView:button];
 if ([button hitTest:p withEvent:event] && started_in_button) {
  started_in_button=NO;
  [button sendActionsForControlEvents:UIControlEventTouchUpInside];
 }
 [hitView touchesEnded:touches withEvent:event];
}

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
 [hitView touchesCancelled:touches withEvent:event];
}

- (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
 hitView = [super hitTest:point withEvent:event];
 return self;
}
person Joe    schedule 28.10.2010

Вот трюк Николая в Swift v3:

Во-первых, сделайте так, чтобы ваш UIViewController реализовывал протокол UIGestureRecognizerDelegate, и добавьте к нему этот метод:

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
    return true
}

Затем примените UITapGestureRecognizer к сборщику, и когда ваша цель будет вызвана, вызовите следующий метод расширения:

extension UIPickerView {
    func pickerTapped(nizer: UITapGestureRecognizer, onPick: @escaping (Int) -> ()) {
        if nizer.state == .ended {
            let rowHeight = self.rowSize(forComponent: 0).height
            let selectedRowFrame = self.bounds.insetBy(dx: 0, dy: (self.frame.height - rowHeight) / 2)

            // check if begin and end tap locations both fall into the row frame:
            if selectedRowFrame.contains(nizer.location(in: self)) &&
                   selectedRowFrame.contains(nizer.location(ofTouch: 0, in: self)) {
                onPick(self.selectedRow(inComponent: 0))
            }
        }
    }
}
person jazzgil    schedule 05.01.2017

  1. Добавьте tapGestureRecognizer в pickerView, как уже было предложено.

  2. Предоставляйте представления для строк средства выбора, а не для заголовка, с помощью

    -(UIView *) pickerView: (UIPickerView *) pickerView viewForRow: (NSInteger) row forComponent: (NSInteger) компонент reusingView: (UIView *) view

  3. В методе обратного вызова жеста сделайте это

-(void)pickerTapped:(UIGestureRecognizer *)gestureRecognizer {

UIPickerView * pv = (id)gestureRecognizer.view;
UIView * selView = [pv viewForRow:[pv selectedRowInComponent:0]
                     forComponent:0];
CGPoint touchPoint = [gestureRecognizer locationInView:selView];
BOOL tapOnSelection = CGRectContainsPoint(selView.bounds, touchPoint);

if (tapOnSelection) ...

}

Я думаю, что это более элегантно, чем пиксельная математика

person Jim75    schedule 09.09.2020