У меня есть UIPickerView
и метод didSelectRow
не вызывается при нажатии на выбранную строку. Мне нужно разобраться с этим делом. Любые идеи?
UIPicker обнаруживает нажатие на текущую выбранную строку
Ответы (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;
}
Ответ Николая в 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
}
Ответ Николая с 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
}
}
}
Решение 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
Вот быстрый код для выяснения, нажал ли пользователь внутри выбранной строки или нет. Я только что преобразовал код 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 ..
}
}
Я решил эту проблему, создав подкласс общего супервизора и перехватив события касания, прежде чем отправлять их вперед. В построителе интерфейсов я также добавил кнопку там, где находится область выбора, прикрепил действие к событию касания и отправил кнопку «назад» (это очень важно, чтобы она не возвращалась в 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;
}
Вот трюк Николая в 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))
}
}
}
}
Добавьте tapGestureRecognizer в pickerView, как уже было предложено.
Предоставляйте представления для строк средства выбора, а не для заголовка, с помощью
-(UIView *) pickerView: (UIPickerView *) pickerView viewForRow: (NSInteger) row forComponent: (NSInteger) компонент reusingView: (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) ...
}
Я думаю, что это более элегантно, чем пиксельная математика