Статьи

iOS SDK: создание пользовательского представления ввода текста

Разработка удивительного приложения не простая работа. Традиционно, одним из самых сложных аспектов этого было создание богатых, привлекательных интерфейсов. Из этого туториала вы узнаете, как создать собственное представление ввода текста, которое поможет сделать ваши собственные приложения блестящими!



Этот учебник является первым в коллекции по созданию пользовательских элементов интерфейса. В этом уроке мы рассмотрим настраиваемые виды ввода текста, а в следующих двух уроках мы рассмотрим пользовательские аккордеонные меню и представления предупреждений. Формат серии следующий:

В этом уроке мы собираемся создать простое приложение со списком, которое будет включать некоторые основные функции. В этом примере наше приложение сможет добавлять и редактировать элементы только с помощью нашего совершенно нового пользовательского представления ввода текста. Окончательный проект этого урока станет основой для следующего, где мы собираемся создать аккордеонное меню, чтобы предоставить пользователю некоторые опции. В третьем и последнем уроке мы будем использовать финальный проект второго урока для создания настраиваемого представления предупреждений.


Начните с запуска Xcode и создайте приложение с одним представлением :

Продолжите и установите имя для проекта. Я назвал мой CustomViewsDemo . Обязательно установите флажок Использовать автоматический подсчет ссылок :

Наконец, выберите папку для сохранения проекта и нажмите « Создать» :


Нажмите на файл ViewController.xib чтобы Интерфейсный ViewController.xib появился на экране. Во-первых, отключите опцию Autolayout (мы делаем это, чтобы этот демонстрационный проект работал на iOS старше 6 лет — Autolayout — это функция iOS 6):

  • Нажмите на кнопку Utilities на панели инструментов Xcode, чтобы показать панель Utilites , если она не видна.
  • Нажмите на инспектора файлов .
  • Немного прокрутите вниз и нажмите на опцию Use Autolayout, чтобы отключить его.

Теперь нажмите на Инспектора Атрибутов и в разделе Simulated Metrics установите значение Size в None , чтобы оно работало и на 3,5-дюймовом экране.

Добавьте UIToolBar в представление и поместите его в нижней части представления. Сделайте следующее:

  • Добавьте элемент кнопки «Гибкая клавиша пробела» слева от элемента кнопки панели по умолчанию, чтобы он оставался с правой стороны панели инструментов.
  • Установите заголовок элемента панели кнопок на Добавить элемент .
  • Установите цвет оттенка кнопки: (R: 51, G: 51, B: 51) .
  • Установите цвет оттенка панели инструментов (R: 0, G: 0, B: 51) .

Затем добавьте UITableView внутри оставшегося доступного пространства представления. Установите его стиль на Grouped, а разделитель на Single Line :

Кроме того, установите его цвет фона (R: 51, G: 51, B: 51) . Теперь ваш интерфейс должен выглядеть так:


Прежде чем идти дальше, нам нужно подключить UITableView к свойству IBOutlet и создать метод IBAction для элемента кнопки панели инструментов (кнопка «Добавить элемент»). Чтобы сделать это, в Интерфейсном Разработчике нажмите на середину Кнопка раздела редактора на панели инструментов Xcode, чтобы открыть Ассистент редактора:

Чтобы вставить новое свойство IBOutlet, удерживая клавишу « Control» + щелкните (щелкните правой кнопкой мыши) в представлении «Таблица»> щелкните «Новый выход ссылки»> «Перетащите и отпустите» в помощнике редактора .

Далее укажите имя для нового свойства. Я просто назвал это table .

Чтобы создать IBAction для элемента кнопки панели, удерживая клавишу « Control» + щелкните (щелкните правой кнопкой мыши) элемент «Кнопка панели» > щелкните параметр «Выбор» в разделе «Отправленные действия»> «Перетащите и отпустите» в помощнике редактора :

Установите имя для метода. Я назвал мой addItem .


На этом этапе нам нужно реализовать минимально необходимые методы табличного представления, чтобы наша таблица работала правильно. Но прежде чем мы это сделаем, нам нужно установить контроллер представления в качестве делегата и источника данных для табличного представления.

Нажмите на файл ViewController.h . Сформируйте заголовок @interface следующим образом:

1
@interface ViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>

Затем в файле ViewController.m и в методе viewDidLoad добавьте следующее:

1
2
3
// Set the tableview’s delegate and datasource.
[_table setDelegate:self];
[_table setDataSource:self];

Массив NSMutableArray будет источником элементов нашего списка (содержимого таблицы). Однако сначала нам нужно объявить и инициализировать его. Внутри файла ViewController.m перейдите к его началу, а внутри закрытой части @interface добавьте следующую строку:

1
2
3
4
5
@interface ViewController ()
 
@property (nonatomic, strong) NSMutableArray *sampleDataArray;
 
@end

Также внутри viewDidLoad :

1
2
// Initialize the sample data array.
_sampleDataArray = [[NSMutableArray alloc] init];

Давайте теперь реализуем некоторые методы табличного представления. Начнем с количества разделов:

1
2
3
4
-(int)numberOfSectionsInTableView:(UITableView *)tableView{
    // Set the number of sections inside the tableview.
    return 1;
}

Далее установите общее количество строк. Это число равно общему количеству объектов в массиве _sampleDataArray .

1
2
3
4
-(int)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    // The number of rows is equal to the number of the sample data in our tableview.
    return [_sampleDataArray count];
}

Давайте установим высоту каждого ряда:

1
2
3
4
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
    // Set the row height.
    return 45.0;
}

Теперь давайте настроим наши клетки. Внутри кода вы найдете все настройки для ячеек. Есть комментарии, объясняющие почти все. Вы можете изменить все, что пожелаете по собственному желанию:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    static NSString *CellIdentifier = @»Cell»;
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
 
        // Let’s set some custom cell options.
        // Set the cell’s background.
        [cell setBackgroundColor:[UIColor colorWithRed:0.8 green:0.8 blue:0.8 alpha:1.0]];
 
        // Set the selection style.
        [cell setSelectionStyle:UITableViewCellSelectionStyleGray];
 
        // Set the accessory type.
        [cell setAccessoryType:UITableViewCellAccessoryDisclosureIndicator];
 
        // Set a font for the cell’s textlabel.
        [[cell textLabel] setFont:[UIFont fontWithName:@»Georgia» size:15.0]];
    }
 
    [[cell textLabel] setText:[_sampleDataArray objectAtIndex:[indexPath row]]];
 
    return cell;
}

Наконец, добавьте -(void)tableView:didSelectRowAtIndexPath: метод делегата:

1
2
3
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
 
}

Прямо сейчас оставьте этот метод пустым. Мы посетим это снова позже.

Подводя итог, мы создали наш проект и настроили интерфейс с помощью Interface Builder. Мы соединили необходимое свойство IBOutlet и метод IBAction и внедрили минимально необходимые методы, чтобы наша таблица (наш список) работала должным образом. Давайте продолжим создавать пользовательский ввод текста. Мы вернемся к ViewController чуть ViewController .


При разработке проектов, это хорошая привычка держать все в порядке и в порядке. По этой причине мы создадим новую группу (виртуальную папку) для контроллера представления ввода текста.

На панели Project Navigator в левой части XCode, нажмите Control + Click (щелчок правой кнопкой мыши) в группе CustomViewsDemo> выберите опцию New Group из всплывающего меню :

Назовите новую группу.

Теперь мы готовы добавить новый контроллер вида. Удерживая клавишу «Control» + щелкните (щелкните правой кнопкой мыши) группу CustomViewsDemo> выберите «Новый файл …» во всплывающем меню :

В качестве шаблона для нового файла выберите класс Objective-C и нажмите Далее.

Дайте имя CustomTextInputViewController в поле класса и убедитесь, что в Подклассе поля выбрано значение UIViewController . Кроме того, не забудьте установить флажок «С XIB для пользовательского интерфейса» :

Наконец, нажмите на кнопку Создать. Убедитесь, что выбранный пользовательский вид ввода текста является выбранной группой, как показано на следующем рисунке:


Нажмите на файл CustomTextInputViewController.xib чтобы открыть Interface Builder и настроить интерфейс. Сделайте следующее:

  • Как и раньше, отключите опцию Autolayout (Панель утилит> Инспектор файлов> Установите флажок Use Autolayout ).
  • Нажмите « Инспектор атрибутов» и в разделе « Имитированные метрики » измените размер на « Нет» .

Теперь пришло время добавить желаемые подпредставления в наше представление. Добавьте следующие подпредставления и установите их свойства, как описано:

  1. UILabel
    • Рамка: X: 0,0, Y: 145,0, ширина: 320,0, высота: 30
    • Шрифт :: Грузия, 17,0
    • Цвет : черный
    • Выравнивание :: Центр
    • Цвет фона: (R: 204, G: 204, B: 204)
  2. UITextField
    • Рамка: X: 0,0, Y: 180,0, ширина: 320,0, высота: 30,0
    • Шрифт: Грузия, 15.0
    • Цвет: черный
    • Бордюр Стиль: Безель
    • Кнопка Очистить: появляется при редактировании
    • Капитализация: предложения
    • Ключ возврата: Готово
    • Цвет фона: белый

    Большинство из этих настроек появятся на следующем изображении:

  3. UIToolbar
    • Рамка: X: 0,0, Y: 225,0, ширина: 320,0, высота: 44,0
    • Цвет Оттенок: (R: 204, G: 204, B: 204)

Обратите внимание, что точка Y сейчас не важна, так как она будет установлена ​​программно для каждого подпредставления.

Помимо вышеупомянутого, добавьте следующие элементы UIBarButton на панель инструментов рядом с элементом кнопки панели по умолчанию:

  • Гибкая клавиша пробела
  • Еще один элемент кнопки панели

Выберите оба элемента панели кнопок (не элемент гибкого пространства) и установите их цвет (R: 51, G: 51, B: 51) . Для кнопки левой панели установите заголовок на Okay . Для кнопки правой панели установите заголовок « Отмена» .

Наконец, выберите все три подпредставления (UILabel, UITextField и UIToolBar) и установите значения автоматического изменения размера, как показано ниже (Гибкая ширина, Гибкое правое поле, Гибкое левое поле и Гибкое нижнее поле):


Ранее я показал вам, как создавать свойства IBOutlet и методы IBAction и как связать их с подпредставлениями. Следуя тому же методу, который описан выше, создайте и подключите следующие свойства к UILabel , UITextField и UIToolbar соответственно:

1
2
3
@property (weak, nonatomic) IBOutlet UILabel *lblTitle;
@property (weak, nonatomic) IBOutlet UITextField *txtText;
@property (weak, nonatomic) IBOutlet UIToolbar *toolbarIAV;

Затем перейдите к объявлению и подключите следующие методы IBAction к кнопкам Okay и Cancel:

1
2
— (IBAction)acceptTextChanges:(id)sender;
— (IBAction)cancelTextChanges:(id)sender;

На этом наша работа с Интерфейсным Разработчиком закончена. Пришло время кодировать поведение, которое нам требуется от представления ввода текста.


Во-первых, нам нужно выполнить какую-то настройку в коде, прежде чем реализовывать какой-либо метод. Мы начнем с установки контроллера представления в качестве делегата для текстового поля. Нажмите на файл CustomTextInputViewController.h и измените заголовок CustomTextInputViewController.h следующим образом:

1
@interface CustomTextInputViewController : UIViewController <UITextFieldDelegate>

Затем перейдите в файл CustomTextInputViewController.m и в методе viewDidLoad установите делегата:

1
2
// Set the textfield delegate.
[_txtText setDelegate:self];

Кроме того, внутри viewDidLoad установите UIToolbar ( toolbarIAV ) в качестве вспомогательного представления ввода текстового поля:

1
2
3
4
5
// Set the textfield delegate.
[_txtText setDelegate:self];
 
// Set the input accessory view of the textfield.
[_txtText setInputAccessoryView:_toolbarIAV];

Все идет нормально. В начале этого урока я упомянул, что представление контроллера представления будет работать в качестве прикрытия с целью не допустить, чтобы пользователи касались чего-либо еще, существующего за нашим представлением. Нам также нужно, чтобы он был полупрозрачным, чтобы он выглядел хорошо. Поэтому перейдите и добавьте следующую строку, которая настраивает наше представление:

1
2
3
4
5
// Set the background color of the view to a semi-transparent gray.
[self.view setBackgroundColor:[UIColor colorWithRed:0.66
                                                  green:0.66
                                                  blue:0.66
                                                  alpha:0.75]];

Теперь давайте подумаем об этом. Как мы уже говорили в начале, нам нужно, чтобы текстовое поле появилось прямо над вспомогательным видом ввода (панель инструментов) и надписью заголовка над текстовым полем. Чтобы добиться такой раскладки, нам нужно знать происхождение клавиатуры и вспомогательного вида ввода, чтобы мы могли соответствующим образом настроить текстовое поле и метку. Но как мы узнаем, когда появится клавиатура, и как мы узнаем ее размер или ее происхождение?

Ну, ответ прост. Каждый раз, когда клавиатура вот-вот должна появиться, iOS отправляет уведомление с именем UIKeyboardWillShowNotification (на самом деле iOS отправляет больше уведомлений, но нас это сейчас не волнует). Мы добавим контроллер представления в качестве наблюдателя для этого уведомления, и когда оно прибудет, будет вызываться предопределенный (нами) метод. Имея это в виду, добавьте следующую строку снова в метод viewDidLoad :

1
2
// Add self as observer to the NSNotificationCenter so we know when the keyboard is about to be shown up.
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShowWithNotification:) name:UIKeyboardWillShowNotification object:nil];

Обратите внимание, что keyboardWillShowWithNotification: это метод, который мы реализуем через некоторое время.

Еще один момент, который мы должны принять во внимание, это строка состояния. Положение, в котором появляются наши подпредставления, зависит от строки состояния и ее высоты. Поэтому было бы неплохо, если бы у нас была переменная, которая сохраняет смещение, созданное строкой состояния (ее высотой), когда она видна.

Перейдите в начало файла и внутри приватной части CGFloat переменную CGFloat которая будет представлять смещение строки состояния. Наряду с этим объявить keyboardWillShowWithNotification: я упоминал чуть ранее:

1
2
3
4
5
6
7
@interface CustomTextInputViewController (){
    CGFloat statusBarOffset;
}
 
-(void)keyboardWillShowWithNotification:(NSNotification *)notification;
 
@end

Я скоро упомяну о статусной строке.

Теперь мы готовы перейти к этапу реализации и можем создать необходимые публичные методы, к которым могут обращаться другие классы. Перейдите в файл CustomTextInputViewController.h и объявите следующие три метода:

1
2
3
4
5
-(void)showCustomTextInputViewInView:(UIView *)targetView
                            withText:(NSString *)text
                            andWithTitle:(NSString *)title;
-(void)closeTextInputView;
-(NSString *)getText;

Должно быть понятно, что будет делать каждый метод.

Давайте начнем с showCustomTextInputViewInView:withText:andWithTitle . Прежде чем мы начнем кодировать, я должен упомянуть несколько моментов. Прежде всего, нам нужно проверить, видна ли строка состояния, и соответствующим образом установить значение statusBarOffset . Мы можем легко узнать, виден ли он, и получить его размер. Однако существует небольшая ловушка, если строка состояния видна, и нам приходится иметь дело как с книжной, так и с альбомной ориентацией. В портретном режиме размер строки состояния имеет вид Ширина х Высота (например, 320,0 х 20,0) . В ландшафтном режиме размер имеет вид Высота х Ширина (например, 20,0 х 480,0) . Самый простой способ определить высоту строки состояния — это проверить минимальное значение между шириной и высотой. С помощью этого метода мы уверены, что получим правильное значение. Добавьте их сразу в следующий фрагмент кода.

Кроме того, вы можете помнить, что мы отключили функцию Autolayout, поэтому мы несем ответственность за правильное представление наших представлений во всех ориентациях. Это означает, что мы должны проверять ориентацию каждый раз, когда вызывается этот метод, и соответственно устанавливать рамку представления. Важно отметить, что когда мы находимся в ландшафтном режиме, ширина экрана — это высота для вида, а высота экрана — это ширина для вида . Кроме того, поскольку строка состояния видна, в левой части представления в альбомном режиме создается отступ, который необходимо устранить. statusBarOffset поэтому мы объявили переменную statusBarOffset ранее.

Наконец, мы хотим, чтобы наш вид вначале был вне поля зрения, а анимации плавно скользили по экрану.

Но сначала давайте посмотрим все в действии. Откройте CustomTextInputViewController.m и реализуйте метод:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
-(void)showCustomTextInputViewInView:(UIView *)targetView withText:(NSString *)text andWithTitle:(NSString *)title{
    if (![[UIApplication sharedApplication] isStatusBarHidden]) {
        CGSize statusBarSize = [[UIApplication sharedApplication] statusBarFrame].size;
        if (statusBarSize.width < statusBarSize.height) {
            // If the width is smaller than the height then this is the value we need.
            statusBarOffset = statusBarSize.width;
        }
        else{
            // Otherwise the height is the desired value we want to keep.
            statusBarOffset = statusBarSize.height;
        }
    }
    else{
        // Otherwise set it to 0.0.
        statusBarOffset = 0.0;
    }
 
    // Before showing the self.view on-screen, we need to calculate the following
    // values, depending always on the orientation.
    CGFloat x, width, height;
 
    if ([[UIApplication sharedApplication] statusBarOrientation] == UIInterfaceOrientationLandscapeLeft ||
        [[UIApplication sharedApplication] statusBarOrientation] == UIInterfaceOrientationLandscapeRight){
 
        // Landscape orientation.
 
        // Set the x point for the view.
        // padding is going to exist at the left of the view when it will appear.
        x = targetView.frame.origin.x — statusBarOffset;
 
        // In landscape orientation, the width of the view equals to the height of the target view.
        width = targetView.frame.size.height;
        // The same with the height.
        height = targetView.frame.size.width;
    }
    else{
        // In portrait orientation everything is normal.
        x = targetView.frame.origin.x;
        width = targetView.frame.size.width;
        height = targetView.frame.size.height;
    }
 
    // Initially set the self.view off screen.
    [self.view setFrame:CGRectMake(x,
                                   -height,
                                   width,
                                   height)];
 
    // Add the view to the target view.
    [targetView addSubview:self.view];
 
    // Begin animating the appearance of the view.
    [UIView beginAnimations:@»» context:nil];
    [UIView setAnimationDuration:0.25];
    [UIView setAnimationCurve:UIViewAnimationCurveLinear];
 
    // We simply want the y point of the origin to become 0.0.
    [self.view setFrame:CGRectMake(self.view.frame.origin.x,
                                   0.0,
                                   self.view.frame.size.width,
                                   self.view.frame.size.height)];
    [UIView commitAnimations];
 
    // Set the textfield as the first responder to make the keyboard appear.
    [_txtText becomeFirstResponder];
 
    // Set the text to edit (if exists) to the textfield.
    [_txtText setText:text];
 
    // Set the label’s text (the title above the textfield).
    [_lblTitle setText:title];
}

Комментарии внутри метода объясняют все. Обратите внимание, что внутри него мы вызываем метод _txtText текстового _txtText , чтобы клавиатура тоже появлялась. Через некоторое время мы увидим, насколько важен этот вызов, так как это ключевой момент для правильного позиционирования наших представлений.

Теперь давайте реализуем следующий метод. Закрытие представления — это действие, противоположное показу.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
-(void)closeTextInputView{
    // Resign the textfield from first responder to make the keyboard go away.
    [_txtText resignFirstResponder];
 
    // Animate the view closing.
    [UIView beginAnimations:@»» context:nil];
    [UIView setAnimationDuration:0.25];
    [UIView setAnimationCurve:UIViewAnimationCurveLinear];
    [self.view setFrame:CGRectMake(self.view.frame.origin.x,
                                   -self.view.frame.size.height,
                                   self.view.frame.size.width,
                                   self.view.frame.size.height)];
    [UIView commitAnimations];
 
    // Also remove the view from the superview after a while (we must let the animation finish first).
    [self.view performSelector:@selector(removeFromSuperview) withObject:nil afterDelay:0.25];
}

Обратите внимание, что мы удалили представление из суперпредставления не сразу, а после окончания анимации.

Наконец, третий метод:

1
2
3
4
-(NSString *)getText{
    // Return the textfield’s text.
    return [_txtText text];
}

Этот следующий шаг покажет вам, как обращаться с клавиатурой. Ранее внутри приватной части интерфейса мы объявили этот метод:

1
-(void)showCustomTextInputViewInView:(UIView *)targetView withText:(NSString *)text andWithTitle:(NSString *)title;

Это метод, который будет вызываться каждый раз, когда должна появиться клавиатура и будет UIKeyboardWillShowNotification уведомление UIKeyboardWillShowNotification . Теперь мы можем начать работать над этим. Параметр notification содержит NSDictionary с информацией о клавиатуре. Часть этой информации — размер и происхождение клавиатуры. Здесь нам на самом деле нужна точка начала, потому что если мы знаем координату Y клавиатуры (включая вспомогательное представление ввода), мы можем правильно расположить текстовое поле и метку. Еще раз, в ландшафтном режиме это изменяется, и координация X клавиатуры — желаемая координация Y для нашего текстового поля. Вот код, который вам понадобится:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
-(void)keyboardWillShowWithNotification:(NSNotification *)notification{
    // Get the userInfo dictionary from the notification object.
    NSDictionary *info = [notification userInfo];
 
    // Get the keyboard origin.
    CGPoint keyboardOrigin = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].origin;
 
    CGFloat keyboardOriginY;
    if ([[UIApplication sharedApplication] statusBarOrientation] == UIInterfaceOrientationLandscapeLeft ||
        [[UIApplication sharedApplication] statusBarOrientation] == UIInterfaceOrientationLandscapeRight){
 
        // In landscape orientation the x point represents the vertical axis.
        keyboardOriginY = keyboardOrigin.x;
    }
    else{
        keyboardOriginY = keyboardOrigin.y;
    }
 
    // Set the appropriate frame for the textfield.
    [_txtText setFrame:CGRectMake(_txtText.frame.origin.x,
                                  keyboardOriginY — _txtText.frame.size.height — statusBarOffset,
                                  _txtText.frame.size.width,
                                  _txtText.frame.size.height)];
 
    // Set the label’s frame in turn.
    [_lblTitle setFrame:CGRectMake(0.0,
                                   _txtText.frame.origin.y — _lblTitle.frame.size.height,
                                   _lblTitle.frame.size.width,
                                   _lblTitle.frame.size.height)];
 
}

Я уверен, что вы заметили вычитание statusBarOffset в коде выше. Если мы этого не сделаем, текстовое поле будет скрыто под представлением вспомогательного ввода.

На этом этапе нам нужно работать над определением протокола. К этому моменту контроллер представления почти готов, но не полностью закончен. Здесь отсутствует определение протокола, которое позволяет классу делегата, который его принимает (в нашем случае ViewController ), реализовать необходимые методы, необходимые для обработки нажатий кнопок « ViewController и «Отмена».

Перейдите в файл CustomTextInputViewController.h и определите протокол вместе со следующими методами делегата:

1
2
3
4
@protocol CustomTextInputViewControllerDelegate
-(void)shouldAcceptTextChanges;
-(void)shouldDismissTextChanges;
@end

Также сразу после заголовка @interface добавьте выделенную строку:

1
2
3
4
@interface CustomTextInputViewController : UIViewController
@property (nonatomic, strong) id delegate;

В соответствии с тем, что мы сделали, класс делегата должен реализовать метод shouldAcceptTextChanges , чтобы обрабатывать нажатие кнопки «Okay», и метод shouldDismissTextChanges , чтобы обрабатывать нажатие кнопки «Отмена».

Но как насчет методов IBAction этого класса? Они были оставлены пустыми до сих пор. Давайте теперь заполним их:

1
2
3
4
5
6
7
— (IBAction)acceptTextChanges:(id)sender {
    [self.delegate shouldAcceptTextChanges];
}
 
— (IBAction)cancelTextChanges:(id)sender {
    [self.delegate shouldDismissTextChanges];
}

Как видите, при нажатии кнопки « shouldAcceptTextChanges метод делегата shouldAcceptTextChanges , а при нажатии кнопки «Отмена» shouldDismissTextChanges метод делегата shouldDismissTextChanges .

Чтобы быть полностью правильным, нужно выполнить еще один шаг: заставить кнопку « Готово» на клавиатуре работать. Для этого нам просто нужно реализовать метод -(BOOL)textFieldShouldReturn: делегат:

1
2
3
4
5
-(BOOL)textFieldShouldReturn:(UITextField *)textField{
    // The tap on the Done button of the keyboard equals to the Okay button.
    [self.delegate shouldAcceptTextChanges];
    return YES;
}

Теперь наш новый пользовательский вид готов. Все, что нам нужно сделать, это попробовать.


Прежде чем мы увидим наш новый пользовательский вид ввода текста, нам нужно выполнить еще несколько шагов. Прежде всего, мы должны объявить класс ViewController качестве его делегата. Откройте файл ViewController.h , импортируйте файл CustomTextInputViewController.h и сделайте наш класс его делегатом в заголовке интерфейса:

1
2
3
4
5
6
7
#import <UIKit/UIKit.h>
#import «CustomTextInputViewController.h»
 
@interface ViewController : UIViewController <UITableViewDelegate, UITableViewDataSource, CustomTextInputViewControllerDelegate>
@property (weak, nonatomic) IBOutlet UITableView *table;

Теперь ViewController.m файлу ViewController.m и внутри приватной части CustomTextInputViewController объект CustomTextInputViewController . Я назвал это textInput :

1
2
3
4
5
6
7
@interface ViewController ()
 
@property (nonatomic, strong) NSMutableArray *sampleDataArray;
 
@property (nonatomic, strong) CustomTextInputViewController *textInput;
 
@end

Давайте инициализируем его внутри метода viewDidLoad :

1
2
3
// Initialize the custom text input object.
_textInput = [[CustomTextInputViewController alloc] init];
[_textInput setDelegate:self];

Перейдите к addItem IBAction, чтобы addItem наше представление ввода текста:

1
2
3
4
5
— (IBAction)addItem:(id)sender {
    [_textInput showCustomTextInputViewInView:self.view
                                    withText:@»»
                                    andWithTitle:@»Add new item»];
}

Просто, правда?

Если вы запустите приложение сейчас, вы увидите представление ввода текста в действии, но кнопки Okay и Cancel не будут работать, потому что мы еще не реализовали методы делегата. Итак, давайте сделаем это сейчас:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
-(void)shouldAcceptTextChanges{
    // Add the new item to the sampleDataArray.
    [_sampleDataArray addObject:[_textInput getText]];
 
    // Reload the table using animation.
    [_table reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationAutomatic];
 
    // Close the text input.
    [_textInput closeTextInputView];
}
 
-(void)shouldDismissTextChanges{
    [_textInput closeTextInputView];
}

Запустите его, и он будет работать. Любой текст, который вы вводите в текстовое поле, появится в таблице. Но мы не полностью готовы, потому что мы не можем редактировать любые элементы.

Пришло время посетить метод делегата табличного представления -(void)tableView:didSelectRowAtIndexPath: Мы хотим, чтобы каждая строка, которая была нажата, была отредактирована в нашем пользовательском представлении ввода текста. Но нам также нужно знать, когда пользователь добавляет или редактирует строку. По этой причине мы будем использовать флаг, который будет указывать, редактирует ли пользователь строку или нет.

Перейдите в приватную часть @interface и добавьте следующую переменную:

Не забудьте про фигурные скобки. Теперь в методе viewDidLoad установите его значение NO .

1
2
// Set the initial value of the isEditingItem flag.
isEditingItem = NO;

Перейдите к методу делегата таблицы:

1
2
3
4
5
6
7
8
9
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
    [_textInput showCustomTextInputViewInView:self.view
                                    withText:[_sampleDataArray objectAtIndex:[indexPath row]]
                                    andWithTitle:@»Edit item»];
 
    // Set the isEditingItem flag value to YES, indicating that
    // we are editing an item.
    isEditingItem = YES;
}

В этот момент, когда вы нажмете на существующие строки в табличном представлении, появится текстовое представление для их редактирования. Но как бы вы ни старались, ваши изменения не будут сохранены в отредактированной строке, а будут сохранены как новые строки. Это означает, что мы должны немного изменить метод делегата -(void)shouldAcceptTextChanges как таковой:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
-(void)shouldAcceptTextChanges{
    // If the isEditingItem flag is set to NO, then a new item has been
    // added to the list.
    if (!isEditingItem) {
        // Add the new item to the sampleDataArray.
        [_sampleDataArray addObject:[_textInput getText]];
    }
    else{
        // Replace the selected item into the array with the updated value.
        NSUInteger index = [[_table indexPathForSelectedRow] row];
        [_sampleDataArray replaceObjectAtIndex:index withObject:[_textInput getText]];
 
        // Set the isEditingItem flag to NO.
        isEditingItem = NO;
    }
 
    // Reload the table using animation.
    [_table reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationAutomatic];
 
    // Close the text input.
    [_textInput closeTextInputView];
}

Помните, что при редактировании содержимого строки эквивалентный объект внутри образца массива данных заменяется. Также обратите внимание на isEditingItem . Он снова получает значение NO .

Теперь все готово к работе. Запустите приложение и проверьте его в книжной и альбомной ориентации.


Как вы, возможно, уже поняли, иногда стоит создавать собственные представления, которые удовлетворяют вашим потребностям лучше, чем стандартные представления iOS SDK. Пользовательский вид ввода текста — это повторно используемый компонент с небольшими изменениями или без них. Его можно использовать во многих проектах, и мы сделали все это, используя простые методы и принципы программирования. Это пользовательское представление ввода текста, теперь доступное каждому, может стать предметом дальнейшей настройки и улучшений, и, надеюсь, оно будет полезно для многих программистов! В следующем уроке мы будем использовать этот проект и добавим новую функцию, меню аккордеона с опциями, предоставленными пользователю. Но пока наслаждайтесь пользовательским видом ввода текста!