Определеяем какая строка была нажата в UIPickerView

в 12:27, , рубрики: ios development, objective-c, Песочница, разработка под iOS, метки: ,
Проблема

Недавно решил преукрасить интерфейс своего приложения элементом UIPickerView, но меня не совсем устроило, то, что мне предложил стандартный набор инструментов, а именно: прокрутить его так чтоб нужный элемент стал под «Selection Indicator» и потом выполнить какое-то действие. Мне нужно было чтоб по нажатию на любую строку посылалось сообщение с нужными параметрами. Поэтому я решил кастомизировать UIPickerView прикрутив к нему UITapGestureRecogniser.

Решение

Ниже я шаг за шагом распишу порядок своих действий. Я работаю с сабклассом UIViewController в котором объявлены протоколы UIPickerViewDelegate и UIPickerViewDataSource.

  1. Добавляем UITapGestureRecogniser в UIPickerView:

    UITapGestureRecognizer *tapgesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(whereAreYouTappedOnPicker:)];
    [self.pickerView addGestureRecognizer:tapgesture];

    Данное действие я выполянл в функции viewDidLoad:. whereAreYouTappedOnPicker: — это селектор, который будет вызываться при нажатии на пикер и вычислять какая строка была нажата.

  2. Объявляем whereAreYouTappedOnPicker: в хэдере (.h файл) вашего сабкласса UIViewController.

    -(void)whereAreYouTappedOnPicker:(UIGestureRecognizer *)gestureRecognizer;

  3. Пишем реализацию функции, которую объявили рание(делаем это в .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: не реагирует на прокрутку при помощи кода, здесь нам и понадобиться «другая функция»

    И так, у нас будет два события:

    1. Когда юзер скролит пикер, то вызывается метод didSelectRow: в котором вызывается метод customPickerView: и как параметр передается индекс строки, которая находиться под «Selection Indicator».
    2. Когда юзер нажимает на нужную строку, то вызывается метод whereAreYouTappedOnPicker: в котором вызывается метод customPickerView: и как параметр передается индекс строки, которая была нажата.

    Код метода didSelectRow:

    - (void)pickerView:(UIPickerView *)pickerView didSelectRow: (NSInteger)row
    inComponent:(NSInteger)component {
    [self customPickerView:pickerView didSelectRow:row inComponent:component
    asResultOfTap:NO];
    }

  4. Объявляем функцию customPickerView: в хэдере.

    -(void)customPickerView:(UIPickerView *)pickerView didSelectRow:
    (NSInteger)row inComponent:(NSInteger)component asResultOfTap:(bool)userTapped;

  5. Пишем реализацию функции 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

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js