UIPicker 检测当前选定行上的点击

Posted

技术标签:

【中文标题】UIPicker 检测当前选定行上的点击【英文标题】:UIPicker detect tap on currently selected row 【发布时间】:2010-08-18 23:04:36 【问题描述】:

我有一个UIPickerView,并且在点击选定行时不会调用方法didSelectRow。我需要处理这个案子。有什么想法吗?

【问题讨论】:

老实说,有点惊讶没有人知道在这里做什么。我能想到的一件事是,您可以将pickerview 子类化并添加自己的触摸方法,但这似乎有点矫枉过正和混乱。 didSelectRow 在一行成为选定行时一直被调用,但如果用户点击(选择)已经选择的行则不会。必须有一种方法可以让我在没有子类化的情况下捕获这种情况...... 类似问题:***.com/questions/567805/… 【参考方案1】:

首先,使类符合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;

【讨论】:

为了使这段代码正常工作,我添加了 -(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer // return return true; 在这里指定:***.com/questions/22319427/… 如果您滚动千分尺然后决定停留在同一个对象上,此代码也会触发。为了解决这个问题,我添加了一个平移手势识别器来确定用户正在滚动。在滚动状态下,我没有在点击识别器回调中做任何事情。 @AdamJohns 我尝试将 PanGesture 添加到我的选择器中。但是 PanGesture 选择器没有调用。我有一个 UITapGestureRecognizer 以及这个选择器视图。您对此有什么想法或建议吗?【参考方案2】:

Nikolay 在 Swift 4 中的回答:

首先,在viewDidLoad() 中将UITapGestureRecognizer 添加到您的UIPickerView,并让您的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”,您可以让它首先检查视图,例如“ifgestureRecognizer.view == self. pickerView return true 返回 false”。如果您在 ViewController 中的其他视图上使用gestureRecognizer,则需要这样做。【参考方案3】:

Nikolay 对 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

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

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
      
    
  

【讨论】:

【参考方案4】:

解决方案 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 上都为我工作

【讨论】:

【参考方案5】:

这是确定用户是否在所选行内点击的快速代码。我刚刚从 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 ..
    

【讨论】:

【参考方案6】:

我通过继承一个通用的超级视图并在发送它们之前拦截触摸事件来解决这个问题。在界面生成器中,我还在选择器区域所在的位置添加了一个按钮,将一个操作附加到了触摸事件,并将按钮发送到“后部”(这非常重要,因此它不会在 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;

【讨论】:

【参考方案7】:

这是 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))
            
        
    

【讨论】:

【参考方案8】:

    按照已经建议的方式向 pickerView 添加一个 tapGestureRecognizer

    提供选择器行的视图,而不是标题,通过

    -(UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)组件重用View:(UIView *)view

    在手势的回调方法中这样做

-(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) ...

我认为比做像素数学更优雅

【讨论】:

以上是关于UIPicker 检测当前选定行上的点击的主要内容,如果未能解决你的问题,请参考以下文章

在缓冲模式下打开行编辑器时检测网格上的单击

Quasar QTable单击可点击行上的操作按钮

如何关闭行上的开关并打开另一行上的另一个开关

模拟网格行上的单击事件

使用选定的 UIPicker 行作为自定义 UITableViewCell UILabel SWIFT 的文本

UIPicker 视图的 UITextfield 问题