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

Проблема

Недавно решил преукрасить интерфейс своего приложения элементом 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, поэтому этот вариант меня устраивает.

Similar posts

AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 6

    +1
    Пожалуйста, используйте для выделения кода, это значительно облегчает чтение Вашего поста.

    - (id) inintWithCount:(int) value ;
    
      0
      А почему, скажем, не использовать для этих целей UIActionSheet? По-моему, это было бы проще и «гайдлайновее», что ли.
        0
        причем тут UIActionSheet?
          0
          Фактически, вы рассказали о том, как написать свой UIActionSheet на основе UIPickerView. Вот мне и интересно, чем не устраивает UIActionSheet.
        0
        Не знаю, Уважаемый, возможно мы о разных вещах говорим, но разница между imageimage
        очевидна.
          0
          Хорошо, понял вас. Но я предлагал такой вариант:

          Only users with full accounts can post comments. Log in, please.