В этом уроке мы создадим 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 здесь .