Проблема
Недавно решил преукрасить интерфейс своего приложения элементом UIPickerView, но меня не совсем устроило, то, что мне предложил стандартный набор инструментов, а именно: прокрутить его так чтоб нужный элемент стал под «Selection Indicator» и потом выполнить какое-то действие. Мне нужно было чтоб по нажатию на любую строку посылалось сообщение с нужными параметрами. Поэтому я решил кастомизировать UIPickerView прикрутив к нему UITapGestureRecogniser.
Решение
Ниже я шаг за шагом распишу порядок своих действий. Я работаю с сабклассом UIViewController в котором объявлены протоколы UIPickerViewDelegate и UIPickerViewDataSource.
- Добавляем UITapGestureRecogniser в UIPickerView:
UITapGestureRecognizer *tapgesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(whereAreYouTappedOnPicker:)];
[self.pickerView addGestureRecognizer:tapgesture];Данное действие я выполянл в функции viewDidLoad:. whereAreYouTappedOnPicker: — это селектор, который будет вызываться при нажатии на пикер и вычислять какая строка была нажата.
- Объявляем whereAreYouTappedOnPicker: в хэдере (.h файл) вашего сабкласса UIViewController.
-(void)whereAreYouTappedOnPicker:(UIGestureRecognizer *)gestureRecognizer;
- Пишем реализацию функции, которую объявили рание(делаем это в .m файле).
-(void)whereAreYouTappedOnPicker:(UIGestureRecognizer *)gestureRecognizer {
//определяем координату куда нажали на UIPickerView
CGPoint tapCoordinate = [gestureRecognizer locationInView:self.pickerView];
//Определяем высоту строки в UIPickerView. Я делю на 5 потому что использую стандартную высотку строки и высоту пикера
CGFloat heightOfPickerRow = self.pickerView.frame.size.height/5;
//объявляем переменную для хранения информации про строку на которую нажали
NSInteger rowForSelectionIndicator =[self.pickerView selectedRowInComponent:0];
//Анализируем нужно ли нам покрутить пикер и поставить нажатую строку под "Selection Indicator"
if (tapCoordinate.y<heightOfPickerRow) {
//данная область отвечает за позицию первой строки в пикере
//проверяем можем ли мы переместиться на данную строку
if ([self.pickerView selectedRowInComponent:0] > 1)
rowForSelectionIndicator -=2;
else
rowForSelectionIndicator = -1; //никаких действий не требуется
}
else if (tapCoordinate.y<2*heightOfPickerRow) {
//данная область отвечает за позицию второй строки в пикере
//проверяем можем ли мы переместиться на данную строку
if ([self.pickerView selectedRowInComponent:0] > 0)
rowForSelectionIndicator -=1;
else
rowForSelectionIndicator = -1; //никаких действий не требуется
}
else if (tapCoordinate.y<3*heightOfPickerRow) {
//данная область отвечает за позицию третьей строки в пикере, той которая уже находиться под "Selection Indicator"
//если это так, то больше ничего не нужно делать
rowForSelectionIndicator = [self.pickerView selectedRowInComponent:0];
}
else if (tapCoordinate.y<4*heightOfPickerRow) {
//данная область отвечает за позицию четвертой строки в пикере
//проверяем можем ли мы переместиться на данную строку
if ([self.pickerView selectedRowInComponent:0] <
([self.pickerView numberOfRowsInComponent:0]-1))
rowForSelectionIndicator +=1;
else
rowForSelectionIndicator = -1; //никаких действий не требуется
}
else {
//данная область отвечает за позицию пятой строки в пикере
//проверяем можем ли мы переместиться на данную строку
if ([self.pickerView selectedRowInComponent:0] <
([self.pickerView numberOfRowsInComponent:0]-2))
rowForSelectionIndicator += 2;
else
rowForSelectionIndicator = -1; //никаких действий не требуется
}
//проверям нужно ли нам выполнять какое-либо действие по нажатии на данную строку
if (rowForSelectionIndicator!=-1) {
//говорим пикеру прокрутиться на нужную строку
//ВНИМАНИЕ - Метод didSelectRow не вызывается когда вы прокручиваете пикер на нужную строку при помощи кода.
[self.pickerView selectRow:rowForSelectionIndicator inComponent:0 animated:YES];
//поэтому нам нужна другая функция
[self customPickerView:self.pickerView didSelectRow:rowForSelectionIndicator inComponent:0
asResultOfTap:YES];
}
}
Далее нам требуется предусмотреть два случая: когда юзер скролит пикер и когда юзер нажимает на нужную строку. Это нам нужно затем, что didSelectRow: не реагирует на прокрутку при помощи кода, здесь нам и понадобиться «другая функция»
И так, у нас будет два события:
- Когда юзер скролит пикер, то вызывается метод didSelectRow: в котором вызывается метод customPickerView: и как параметр передается индекс строки, которая находиться под «Selection Indicator».
- Когда юзер нажимает на нужную строку, то вызывается метод whereAreYouTappedOnPicker: в котором вызывается метод customPickerView: и как параметр передается индекс строки, которая была нажата.
Код метода didSelectRow:
- (void)pickerView:(UIPickerView *)pickerView didSelectRow: (NSInteger)row
inComponent:(NSInteger)component {
[self customPickerView:pickerView didSelectRow:row inComponent:component
asResultOfTap:NO];
} - Объявляем функцию customPickerView: в хэдере.
-(void)customPickerView:(UIPickerView *)pickerView didSelectRow:
(NSInteger)row inComponent:(NSInteger)component asResultOfTap:(bool)userTapped; - Пишем реализацию функции customPickerView: в .m файле
- (void)customPickerView:(UIPickerView *)pickerView
didSelectRow:(NSInteger)row
inComponent:(NSInteger)component
asResultOfTap:(bool)userTapped
{
if (userTapped) //если юзер нажал на нужную ему строку
{
NSLog(@"нажато на %i", row);}
else //если юзер прокрутил барабан и поместил под "Selection Indicator" нужную строку
{
NSLog(@"выбрано %i", row);
}
}
Проблемы
Иногда при быстром нажатии на пикер selectedRow становится равным -1. У меня при нажатии на строку выполняется переход на новый View, поэтому этот вариант меня устраивает.
Автор: eXhausted