В этом уроке мы создадим Jabber-клиент для iOS. Приложение, разработанное в этой серии, позволит пользователям входить в систему, добавлять друзей и отправлять сообщения. Этот учебник будет посвящен настройке пользовательского интерфейса для примера клиента чата.
Обзор iOS-клиента
Ядро нашего приложения Jabber построено на возможностях XMPP. Мы будем хранить эти функции в главном делегате приложения, который будет реализовывать собственный протокол для отправки событий, таких как вход в систему и отправка сообщений. Приложение, которое мы собираемся создать, основано на трех видах: логин, список друзей и чат.

Список друзей — это представление по умолчанию, которое отображается при запуске приложения. Он показывает список онлайн-друзей. Вид входа в систему появится только в том случае, если на устройстве ранее не было сохранено никаких учетных данных. Кнопка с именем «Аккаунт» покажет вид входа в систему из списка друзей, чтобы при необходимости можно было изменить учетные данные для входа. Вид чата отображается при нажатии на собеседника в сети, чтобы начать чат. Мы создадим контроллер представления для каждого из этих представлений. Контроллеры реализуют простой протокол для получения уведомлений, отправляемых делегатом приложения. Для простоты вход в систему и просмотр чата будут отображаться как модальный контроллер просмотра. Если хотите, вы можете переработать приложение, чтобы вместо него использовать навигационный контроллер.
Настройка проекта
Давайте откроем Xcode и начнем новый проект. Мы выберем приложение на основе простого представления и назовем его «JabberClient». Чтобы взаимодействовать с сервером, мы примем удобную библиотеку для iOS, которая называется фреймворк XMPP . Эта библиотека совместима с приложениями Mac и iOS и поможет нам в реализации низкоуровневых функций для подключения к серверу XMPP и управления обменом сообщениями через сокеты. Поскольку в репозитории нет ссылки на скачивание, вам нужно установить git (см. Здесь для получения дополнительной информации). После того, как вы установили git, вы можете ввести в консоли следующую команду:
git clone https://code.google.com/p/xmppframework/ xmppframework
После завершения загрузки мы должны получить папку, подобную следующей:

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

Нам не нужна интеграция с Facebook, поэтому в группе «Расширения» мы можем удалить папку «X-FACEBOOK-PLATFORM».

Теперь давайте добавим необходимые рамки. Мы выбираем проект в навигаторе, затем выбираем цель и открываем «Link Binary With Libraries», как показано на рисунке.

Мы должны добавить множество фреймворков, как показано на следующем рисунке:

Наконец, для компиляции проекта нам нужно настроить некоторые параметры сборки. Изменения должны быть добавлены как к проекту, так и к цели. Сначала мы находим «Пути поиска по заголовкам пользователей» и указываем библиотеку, необходимую для разбора xml: ‘/ usr / include / libxml2’

Затем мы выбираем «Другие флаги компоновщика» и добавляем следующий флаг: «-lxml2».

Теперь проект настроен правильно, и вы сможете его построить без ошибок и предупреждений.
Создание представления списка друзей
Список друзей содержит табличное представление, которое показывает список онлайн-контактов. При нажатии на него отображается соответствующий вид чата. Мастер проекта уже создал контроллер представления, который мы будем переименовывать в BuddyListViewController для большей согласованности. Этот контроллер будет иметь UITableView и массив для хранения онлайн-контактов. Он также будет иметь IBAction для отображения вида входа в систему, если пользователь хочет сменить аккаунт. Кроме того, он будет реализовывать делегаты табличного представления Поэтому мы обновляем файл реализации следующим образом.
|
01
02
03
04
05
06
07
08
09
10
11
12
|
@interface JabberClientViewController : UIViewController <UITableViewDelegate, UITableViewDataSource> {
UITableView *tView;
NSMutableArray *onlineBuddies;
}
@property (nonatomic,retain) IBOutlet UITableView *tView;
— (IBAction) showLogin;
@end
|
В файле реализации мы синхронизируем свойство и добавляем стандартные методы для управления
вид таблицы.
|
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
|
@implementation JabberClientViewController
@synthesize tView;
— (void)viewDidLoad {
[super viewDidLoad];
self.tView.delegate = self;
self.tView.dataSource = self;
onlineBuddies = [[NSMutableArray alloc ] init];
}
— (void) showLogin {
// show login view
}
#pragma mark —
#pragma mark Table view delegates
— (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *s = (NSString *) [onlineBuddies objectAtIndex:indexPath.row];
static NSString *CellIdentifier = @»UserCellIdentifier»;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
cell.textLabel.text = s;
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
return cell;
}
— (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [onlineBuddies count];
}
— (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
— (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// start a chat
}
@end
|
Соответствующий файл xib будет иметь табличное представление и панель инструментов с элементом кнопки панели, как показано на следующем рисунке:

Мы должны помнить, чтобы связать табличное представление и действие showLogin с их соответствующими выходами, как показано ниже:

Если мы запустим приложение, мы должны увидеть пустую таблицу, как на следующем скриншоте:

Мы можем приостановить реализацию этого класса на некоторое время. Когда мы будем готовы, мы интегрируем функции XMPP. А пока давайте перейдем к логину.
Создание пользовательского интерфейса входа
Это представление отображается, когда пользователь еще не ввел учетные данные для входа или когда нажата кнопка «Учетная запись». Он состоит из двух полей ввода и кнопки. Дополнительное действие позволит пользователю скрыть представление без изменений.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
@interface SMLoginViewController : UIViewController {
UITextField *loginField;
UITextField *passwordField;
}
@property (nonatomic,retain) IBOutlet UITextField *loginField;
@property (nonatomic,retain) IBOutlet UITextField *passwordField;
— (IBAction) login;
— (IBAction) hideLogin;
@end
|
Реализация довольно проста. Когда инициируется действие входа в систему, данные в NSUSerDefaults сохраняются в NSUSerDefaults с двумя ключами «userID» и «userPassword». Эти данные будут использованы механизмом XMPP и отправлены на сервер.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
@implementation SMLoginViewController
@synthesize loginField, passwordField;
— (IBAction) login {
[[NSUserDefaults standardUserDefaults] setObject:self.loginField.text forKey:@»userID»];
[[NSUserDefaults standardUserDefaults] setObject:self.passwordField.text forKey:@»userPassword»];
[[NSUserDefaults standardUserDefaults] synchronize];
[self dismissModalViewControllerAnimated:YES];
}
— (IBAction) hideLogin {
[self dismissModalViewControllerAnimated:YES];
}
@end
|
Как и выше, мы не должны связывать текстовые поля и действия в файле XIB.

Теперь мы можем обновить контроллер BuddyList, чтобы при необходимости отображать вид входа в систему. Мы импортируем соответствующий класс и обновляем действие следующим образом.
|
1
2
3
4
5
6
|
— (IBAction) showLogin {
SMLoginViewController *loginController = [[SMLoginViewController alloc] init];
[self presentModalViewController:loginController animated:YES];
}
|
Мы также реализуем функцию viewDidAppear чтобы она отображала представление входа в систему, когда данные не сохраняются.
|
01
02
03
04
05
06
07
08
09
10
11
12
|
— (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSString *login = [[NSUserDefaults standardUserDefaults] objectForKey:@»userID»];
if (!login) {
[self showLogin];
}
}
|
Если мы скомпилируем приложение, то увидим, что окно входа в систему выглядит так, как ожидается, или когда пользователь нажимает кнопку.
Создание чата
Представление чата имеет четыре визуальных элемента:
- панель инструментов с кнопкой, чтобы закрыть вид
- текстовое поле для ввода сообщений
- кнопка для отправки сообщений
- табличное представление для отображения отправленных и полученных сообщений

Заголовочный файл имеет следующий вид:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
@interface SMChatViewController : UIViewController {
UITextField *messageField;
NSString *chatWithUser;
UITableView *tView;
NSMutableArray *messages;
}
@property (nonatomic,retain) IBOutlet UITextField *messageField;
@property (nonatomic,retain) NSString *chatWithUser;
@property (nonatomic,retain) IBOutlet UITableView *tView;
— (id) initWithUser:(NSString *) userName;
— (IBAction) sendMessage;
— (IBAction) closeChat;
@end
|
Подобно представлению друзей, этот класс реализует делегаты таблицы. Он отслеживает полученное с помощью строковой переменной chatWithUser и имеет два действия: closeChat и sendMessage . Соответствующая реализация заключается в следующем.
|
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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
|
@implementation SMChatViewController
@synthesize messageField, chatWithUser, tView;
— (void)viewDidLoad {
[super viewDidLoad];
self.tView.delegate = self;
self.tView.dataSource = self;
messages = [[NSMutableArray alloc ] init];
[self.messageField becomeFirstResponder];
}
#pragma mark —
#pragma mark Actions
— (IBAction) closeChat {
[self dismissModalViewControllerAnimated:YES];
}
— (IBAction)sendMessage {
NSString *messageStr = self.messageField.text;
if([messageStr length] > 0) {
// send message through XMPP
self.messageField.text = @»»;
NSString *m = [NSString stringWithFormat:@»%@:%@», messageStr, @»you»];
NSMutableDictionary *m = [[NSMutableDictionary alloc] init];
[m setObject:messageStr forKey:@»msg»];
[m setObject:@»you» forKey:@»sender»];
[messages addObject:m];
[self.tView reloadData];
[m release];
}
}
#pragma mark —
#pragma mark Table view delegates
— (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSDictionary *s = (NSDictionary *) [messages objectAtIndex:indexPath.row];
static NSString *CellIdentifier = @»MessageCellIdentifier»;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
}
cell.textLabel.text = [s objectForKey:@»msg»];
cell.detailTextLabel.text = [s objectForKey:@»sender»];
cell.accessoryType = UITableViewCellAccessoryNone;
cell.userInteractionEnabled = NO;
return cell;
}
— (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [messages count];
}
— (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
#pragma mark —
#pragma mark Chat delegates
// react to the message received
— (void)dealloc {
[messageField dealloc];
[chatWithUser dealloc];
[tView dealloc];
[super dealloc];
}
|
Когда представление загружено, мы показываем клавиатуру. Часть таблицы очень похожа на вид друзей. Здесь мы используем немного другой тип ячейки таблицы для отображения как сообщения, так и имени. Ниже приведен предполагаемый результат, когда приложение готово:

Не забудьте связать свойства IBAction с соответствующими кнопками как обычно.

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