UIPicker如何检测当前选定行的点击事件?

47

我有一个 UIPickerView,当点击已选择的时,方法didSelectRow不被调用。 我需要处理这种情况。 有什么想法吗?


1
说实话,有点惊讶没有人知道该怎么做。我能想到的一件事是,你可以子类化pickerview并添加自己的touches方法,但这似乎有点过度设计和混乱。当某一行成为选中行时,didSelectRow会一直被调用,但如果用户点击(选择)已经选中的行,则不会被调用。必须有一种方法可以钩入以捕获此情况,而无需子类化... - Royce
类似问题:https://dev59.com/M0bRa4cB1Zd3GeqP2KA8 - andy boot
8个回答

29

首先,将该类符合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;
}

2
为使此代码生效,我需要添加以下内容:-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{ // return return true; }具体信息请参考此链接:https://dev59.com/QGEh5IYBdhLWcg3whTwR?lq=1 - rsc
1
如果您滚动了一微米然后决定停留在同一个对象上,此代码也会触发。为了解决这个问题,我添加了一个平移手势识别器来确定用户是否正在滚动。在滚动状态下,我没有在点击识别器回调中执行任何操作。 - Adam Johns
@AdamJohns 我尝试给我的选择器添加PanGesture,但是PanGesture选择器没有被调用。我还有一个UITapGestureRecognizer与这个picker视图一起。你有任何想法或建议吗? - AnujAroshA

24

Nikolay在Swift 4中的回答:

首先,在viewDidLoad()中为您的UIPickerView添加一个UITapGestureRecognizer,并让您的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)
        }
    }
}

UIGestureRecognizerDelegate 中添加 shouldRecognizeSimultaneouslyWith 方法:

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

不要在 func gestureRecognizer(...shouldRecognizeSimultaneouslyWith otherGestureRecognizer...) 中写入 "return true",而是可以先检查视图,例如使用 "if gestureRecognizer.view == self.pickerView {return true} return false"。如果您在 ViewController 上使用 gestureRecognizer 的其他视图,则需要这样做。 - iOS_Mouse

10

Nikolay的答案使用Swift

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

重要提示:让您的视图控制器确认 UIGestureRecognizerDelegate,否则处理程序将不会触发:

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

最后一步将是点击事件的处理程序:

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
      }
    }
  }

5

方案 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上对我有效。


1
这里是关于确定用户是否点击了所选行的 Swift 代码。我刚刚将 Nikolay 回答中的 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 ..
    }
}

0
我通过子类化一个共同的父视图并在发送触摸事件之前拦截它们来解决了这个问题。在界面构建器中,我还添加了一个按钮,将选择区域附加到触摸上事件,并将按钮发送到“后面”(这非常重要,以便它不会在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;
}

0

这是 Nikolay 在 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))
            }
        }
    }
}

0
  1. 按照之前的建议,为pickerView添加一个tapGestureRecognizer

  2. 通过-(UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component 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) ...

}

我认为这比进行像素计算更优雅。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接