Как и в каждом основном выпуске, iOS 7 включает в себя множество новых API, которые разработчики могут использовать в своих приложениях. В этом уроке мы рассмотрим совершенно новый фреймворк, представленный в iOS 7, фреймворк Multipeer Connectivity. Эта структура добавляет поддержку для обнаружения, подключения и связи с близлежащими службами, такими как устройства iOS. В этом уроке я покажу вам, как создать простую многопользовательскую игру с использованием этого нового фреймворка.
Вступление
Широкий спектр новых API был представлен в iOS 7, предоставляя разработчикам новые возможности и возможности. Фреймворк, который привлек мое внимание, это фреймворк Multipeer Connectivity. Он предлагает ряд полезных функций, которые я хотел бы продемонстрировать в этом уроке.
1. Обзор структуры
Прежде чем мы начнем работать с платформой Multipeer Connectivity, важно сначала ознакомиться с этой платформой. Цель платформы — позволить соседним устройствам связываться друг с другом, что включает в себя обмен данными, такими как изображения и видео.
Важно понимать значение термина поблизости в контексте структуры многопользовательского подключения. Инфраструктура определяет близлежащие устройства как устройства, которые находятся в одной сети Wi-Fi или могут общаться через Bluetooth. Платформа многопользовательского подключения, например, не может использоваться для связи между устройствами через Интернет. Это не то, для чего была разработана платформа Multipeer Connectivity.
Прежде чем два устройства смогут начать обмен данными, им сначала необходимо подключиться друг к другу. Это включает в себя две фазы: 1
обнаружение соседних устройств и 2
установление сеанса между устройствами. На этапе обнаружения одно устройство выполняет поиск или ищет устройства в непосредственной близости. Если устройство хочет быть обнаруживаемым, оно должно рекламировать себя. Это позволяет устройству просмотра находить рекламное устройство.
Когда первое устройство находит рекламное устройство, оно отправляет на устройство запрос на установление соединения, которое рекламодатель может принять или отклонить. Принимая приглашение, сеанс устанавливается, что означает, что устройства могут начать обмен данными. Соединение между двумя устройствами имеет одно из трех возможных состояний: 1
не подключено, 2
подключено или 2
подключено. В любое время устройство может завершить сеанс, который закрывает соединение между устройствами.
просмотр
Структура Multipeer Connectivity предоставляет два пути для просмотра других устройств. Первый вариант — использовать специализированный подкласс контроллера представления, который сделает все за вас. Это подход, который мы будем использовать в этом уроке. Отправка приглашения и организация сеанса — это все, что вам нужно. Конечно, это не подходит для каждого варианта использования. Другой вариант — вручную реализовать логику для поиска ближайших устройств. Это, однако, тема, которую мы не будем освещать в этом руководстве.
В контексте структуры многопользовательского подключения устройство называется одноранговым peerID
и каждый одноранговый узел имеет уникальный идентификатор или идентификатор peerID
и отображаемое имя. Последнее — это имя, которое пользователь дал своему устройству, например, «iPhone Габриэля». Отображаемое имя — важная часть информации, так как это единственный способ, которым пользователи узнают, какое устройство кому принадлежит.
Данные
Данные, которые могут быть переданы между одноранговыми узлами, ограничены тремя типами, 1
экземплярами NSData
, 2
ресурсами, такими как файлы и документы, и 3
потоковыми данными. В этом уроке я покажу вам, как отправлять и получать объекты данных, то есть экземпляры NSData
. Мы не будем рассматривать ресурсы и потоки в этом руководстве.
Данные могут быть отправлены с использованием одного из двух режимов: надежного режима, который медленнее, но гарантирует, что переданные данные получены получателем, и ненадежен, что намного более производительно, но не гарантирует, что данные, отправленные один одноранговый узел получен другим или получен в том порядке, в котором он был отправлен. Какой режим вы выберете, зависит от потребностей вашего приложения.
Это все, что вам нужно знать о среде многопользовательского подключения, чтобы начать работу. Позвольте мне закончить это введение упоминанием четырех классов, с которыми мы будем работать в этом уроке.
-
MCPeerID
: экземпляр этого класса представляет одноранговый узел в многопользовательском сеансе. -
MCSession
: этот класс используется для управления связью между пирами в сеансе. -
MCAdvertiserAssistant
: этот класс используется для помощи в рекламе одноранговыхMCAdvertiserAssistant
на соседних устройствах. -
MCBrowserViewController
: этот подклассUIViewController
заботится о представлении соседних устройств пользователю и помогает установить многопользовательский сеанс.
2. Обзор приложения
Давайте кратко рассмотрим пример приложения, которое мы собираемся создать. Цель этого руководства — показать, как установить соединение между соседними устройствами и обмениваться данными, например NSData
, между подключенными устройствами.
Я уверен, что вы можете подумать о многих случаях использования инфраструктуры Multipeer Connectivity. Однако в этом уроке я выбрал очень простую игру под названием « Угадай секретный номер» . В игре один человек выбирает число в пределах заранее определенного диапазона, который другие игроки в игре должны угадать. После каждого предположения хозяин игры, человек, который выбрал число, сообщает игроку, правильно ли она сделала предположение, и является ли секретное число большим или меньшим. Это достаточно просто, верно?
3. Настройка проекта
Давайте начнем с создания нового проекта в XCode, как показано ниже. Выберите шаблон приложения « Один вид» из списка шаблонов в категории « Приложение iOS » и нажмите « Далее» .
Дайте название вашему проекту, введите идентификатор компании и выберите iPhone в меню « Устройства» . Нажмите кнопку Next
, чтобы продолжить.
Сообщите Xcode, где вы хотите сохранить файлы проекта, и нажмите кнопку « Создать» , чтобы продолжить.
4. Обработка многопользовательского подключения
Существует довольно много стандартного кода, который нам нужно написать, чтобы установить и управлять многопользовательским сеансом. Чтобы не повторяться, мы централизуем эту логику в пользовательском классе, который мы можем использовать в нашем проекте. Вот что мы будем делать в этом разделе.
Шаг 1
Создайте новый класс в XCode, выбрав New> File … в меню File . Выберите класс Objective-C из категории iOS Cocoa Touch и нажмите « Далее» .
Назовите новый класс MPCHandler
, установите для его суперкласса значение NSObject
и нажмите кнопку « Далее» . Скажите Xcode, где вы хотите хранить файлы классов, и нажмите « Create
.
Шаг 2
Откройте файл MPCHandler
класса MPCHandler
и начните с добавления оператора импорта для платформы Multipeer Connectivity, как показано ниже.
1
2
3
4
5
6
|
#import <Foundation/Foundation.h>
#import <MultipeerConnectivity/MultipeerConnectivity.h>
@interface MPCHandler : NSObject
@end
|
Поскольку класс MPCHandler
будет отвечать за управление многопользовательским сеансом, он должен соответствовать протоколу MCSessionDelegate
, как показано ниже. Мы также объявляем четыре свойства, которые помогут нам в этой задаче.
01
02
03
04
05
06
07
08
09
10
11
|
#import <Foundation/Foundation.h>
#import <MultipeerConnectivity/MultipeerConnectivity.h>
@interface MPCHandler : NSObject <MCSessionDelegate>
@property (nonatomic, strong) MCPeerID *peerID;
@property (nonatomic, strong) MCSession *session;
@property (nonatomic, strong) MCBrowserViewController *browser;
@property (nonatomic, strong) MCAdvertiserAssistant *advertiser;
@end
|
Обратите внимание, что это те классы, которые я упоминал ранее в этом уроке. Объект peerID
будет представлять устройство, и он также будет включен в каждую связь между одноранговыми узлами вместе с любыми данными, которыми обмениваются одноранговые узлы. session
объект содержит и управляет многопользовательским сеансом. Мы поговорим об этом классе позже в этом уроке. Объект browser
содержит ссылку на экземпляр MCBrowserViewController
, который мы будем использовать для обнаружения соседних устройств и создания многопользовательского сеанса между ними. Объект advertiser
заботится о рекламе устройства, а также об обработке входящих приглашений.
Прежде чем мы взглянем на реализацию класса MPCHandler
, нам нужно объявить четыре метода, как показано ниже. Первый метод, setupPeerWithDisplayName:
принимает один аргумент — отображение или публичное имя, которое будет использоваться для устройства. Метод advertiseSelf:
также принимает один аргумент, чтобы указать, должно ли устройство быть видимым для других устройств. Метод setupSession
будет отвечать за настройку многопользовательского сеанса, а метод setupBrowser
будет отвечать за настройку экземпляра MCBrowserViewController
.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
#import <Foundation/Foundation.h>
#import <MultipeerConnectivity/MultipeerConnectivity.h>
@interface MPCHandler : NSObject <MCSessionDelegate>
@property (nonatomic, strong) MCPeerID *peerID;
@property (nonatomic, strong) MCSession *session;
@property (nonatomic, strong) MCBrowserViewController *browser;
@property (nonatomic, strong) MCAdvertiserAssistant *advertiser;
— (void)setupPeerWithDisplayName:(NSString *)displayName;
— (void)setupSession;
— (void)setupBrowser;
— (void)advertiseSelf:(BOOL)advertise;
@end
|
Давайте начнем с реализации метода setupPeerWithDisplayName:
В этом методе мы создаем экземпляр класса MCPeerID
и передаем его displayName
. Вот и все.
1
2
3
|
— (void)setupPeerWithDisplayName:(NSString *)displayName {
self.peerID = [[MCPeerID alloc] initWithDisplayName:displayName];
}
|
В setupSession
мы создаем экземпляр MCSession
путем передачи peerID
экземпляра peerID
и устанавливаем делегат сеанса в наш экземпляр MPCHandler
. Важно, чтобы мы setupPeerWithDisplayName:
перед настройкой сеанса, потому что переменная экземпляра peerID
не может быть nil
.
1
2
3
4
|
— (void)setupSession {
self.session = [[MCSession alloc] initWithPeer:self.peerID];
self.session.delegate = self;
}
|
Реализация setupBrowser
не может быть проще. Все, что мы делаем в этом методе, это инициализируем экземпляр MCBrowserViewController
, передавая тип сервиса и сеанс, который мы создали в setupSession
. Первый аргумент — это тип службы для поиска. Он однозначно идентифицирует службу и должен содержать от 1 до 15 символов. Обратите внимание, что тип сервиса может содержать только строчные буквы ASCII, цифры и дефисы. Мы сохраняем ссылку на контроллер представления браузера в переменной экземпляра _browser
.
1
2
3
|
— (void)setupBrowser {
self.browser = [[MCBrowserViewController alloc] initWithServiceType:@»my-game» session:_session];
}
|
В advertiseSelf:
мы инициализируем экземпляр класса MCAdvertiserAssistant
, передавая тот же тип сервиса, который мы использовали для создания экземпляра MCBrowserViewController
. Мы также передаем объект сеанса и устанавливаем информацию об обнаружении nil
так как мы не предоставляем информацию об обнаружении устройства соседним устройствам на этапе обнаружения.
Если для параметра advertise
в advertiseSelf:
задано значение YES
, мы вызываем start
объекта MCAdvertiserAssistant
чтобы запустить помощника и начать рекламу службы. Если для advertise
задано значение NO
, мы останавливаем помощника рекламодателя и устанавливаем для свойства advertiser
nil
.
01
02
03
04
05
06
07
08
09
10
|
— (void)advertiseSelf:(BOOL)advertise {
if (advertise) {
self.advertiser = [[MCAdvertiserAssistant alloc] initWithServiceType:@»my-game» discoveryInfo:nil session:self.session];
[self.advertiser start];
} else {
[self.advertiser stop];
self.advertiser = nil;
}
}
|
Шаг 3
Возможно, вы заметили, что XCode отображает несколько предупреждений, так как мы еще не реализовали необходимые методы MCSessionDelegate
. Давайте избавимся от этих предупреждений путем реализации протокола MCSessionDelegate
. Методы, которые мы реализуем, показаны ниже.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
— (void)session:(MCSession *)session peer:(MCPeerID *)peerID didChangeState:(MCSessionState)state {
}
— (void)session:(MCSession *)session didReceiveData:(NSData *)data fromPeer:(MCPeerID *)peerID {
}
— (void)session:(MCSession *)session didStartReceivingResourceWithName:(NSString *)resourceName fromPeer:(MCPeerID *)peerID withProgress:(NSProgress *)progress {
}
— (void)session:(MCSession *)session didFinishReceivingResourceWithName:(NSString *)resourceName fromPeer:(MCPeerID *)peerID atURL:(NSURL *)localURL withError:(NSError *)error {
}
— (void)session:(MCSession *)session didReceiveStream:(NSInputStream *)stream withName:(NSString *)streamName fromPeer:(MCPeerID *)peerID {
}
|
Давайте посмотрим на каждый метод протокола.
-
session:peer:didChangeState:
этот метод делегата вызывается каждый раз, когда изменяется состояние соединенияsession:peer:didChangeState:
. Помните, что есть три возможных состояния: не подключен, подключен и подключен. Мы будем использовать этот метод делегата для отслеживания пиров, которые подключаются и отключаются от игры. -
session:didReceiveData:fromPeer:
этот метод вызывается каждый раз, когда устройство получает данные от другого узла. -
session:didStartReceivingResourceWithName:fromPeer:withProgress:
Когда приложение начало получать ресурс, такой как файл, от другого узла, этот метод вызывается. Мы не будем описывать этот метод в этом уроке. -
session:didFinishReceivingResourceWithName:fromPeer:atURL:withError:
Как следует из его названия, этот метод вызывается, когда ресурс был получен нашим приложением. - session: didReceiveStream: withName: fromPeer :: Этот метод вызывается, когда приложение получает поток. Поскольку мы не будем работать с потоками, мы также не будем рассматривать этот метод.
Несмотря на то, что мы не будем использовать последние три метода делегата в этом руководстве, нам нужно реализовать их в нашем контроллере представления, чтобы избавиться от предупреждений компилятора.
В session:peer:didChangeState:
и session:didReceiveData:fromPeer:
мы session:didReceiveData:fromPeer:
уведомление с помощью центра уведомлений, чтобы убедиться, что любая часть приложения, которая заинтересована в событии, может быть уведомлена о событии. Это позволяет контроллерам представления нашего приложения добавлять себя в качестве наблюдателя этих уведомлений и отвечать на них, если это необходимо. Это также означает, что реализация этих методов делегата довольно проста, как вы можете видеть ниже.
01
02
03
04
05
06
07
08
09
10
|
— (void)session:(MCSession *)session peer:(MCPeerID *)peerID didChangeState:(MCSessionState)state {
NSDictionary *userInfo = @{ @»peerID»: peerID,
@»state» : @(state) };
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:@»MPCDemo_DidChangeStateNotification»
object:nil
userInfo:userInfo];
});
}
|
Мы начинаем с создания словаря userInfo
уведомления и userInfo
его, передавая словарь в качестве третьего аргумента postNotificationName:object:userInfo:
Обратите внимание, что мы публикуем уведомление внутри блока dispatch_async
чтобы гарантировать, что уведомление будет опубликовано в главном потоке. Это важно, поскольку мы не можем гарантировать, что метод делегата вызывается в основном потоке. Уведомление, однако, должно быть размещено в главном потоке, чтобы убедиться, что каждый наблюдатель получает уведомление.
Реализация session:didReceiveData:fromPeer:
выглядит очень похоже, как вы можете видеть ниже.
01
02
03
04
05
06
07
08
09
10
|
— (void)session:(MCSession *)session didReceiveData:(NSData *)data fromPeer:(MCPeerID *)peerID {
NSDictionary *userInfo = @{ @»data»: data,
@»peerID»: peerID };
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:@»MPCDemo_DidReceiveDataNotification»
object:nil
userInfo:userInfo];
});
}
|
Класс MPCHandler
готов к использованию, поэтому давайте инициализируем его в MPCHandler
приложения.
Шаг 4
Откройте AppDelegate.h, добавьте оператор импорта для класса MPCHandler
и объявите свойство типа MPCHandler
.
01
02
03
04
05
06
07
08
09
10
11
|
#import <UIKit/UIKit.h>
#import «MPCHandler.h»
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@property (nonatomic, strong) MPCHandler *mpcHandler;
@end
|
Переключитесь на файл AppDelegate
класса AppDelegate
и инициализируйте переменную экземпляра в application:didFinishLaunchingWithOptions:
1
2
3
4
5
|
— (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.mpcHandler = [[MPCHandler alloc] init];
return YES;
}
|
5. Опции View Controller
Как я упоминал ранее, приложение будет иметь два контроллера представления. Шаблон единого представления Xcode имеет только один по умолчанию. Давайте добавим второй контроллер вида прямо сейчас. Выберите « Создать»> «Файл …» в меню « Файл» , выберите класс Objective-C в категории « Какао для iOS» слева и нажмите « Далее» .
Убедитесь, что новый класс наследуется от UIViewController
и установите для поля Class значение OptionsViewController . Флажки должны оставаться не отмеченными. Нажмите Далее, чтобы продолжить.
Скажите Xcode, где вы хотите сохранить файлы классов, и нажмите « Create
.
6. Создать пользовательский интерфейс
Пришло время создать пользовательский интерфейс приложения. Откройте основную раскадровку проекта (Main.storyboard), чтобы начать.
Шаг 1
Выберите контроллер представления в раскадровке и выберите « Встроить»> «Контроллер навигации» в меню « Редактор» . В результате контроллер представления встроен в контроллер навигации, а панель навигации добавлена в верхнюю часть контроллера представления.
Выберите панель навигации контроллера представления, откройте инспектор атрибутов справа и установите в поле « Заголовок» значение MPCDemo.
Перетащите два экземпляра UIBarButtonItem
из библиотеки объектов на панель навигации и обновите их заголовки, как показано ниже.
Это представление, которое игроки увидят и используют для ввода предположения, отправки его другим игрокам и отслеживания предыдущих предположений, сделанных в игре. Чтобы сделать все это возможным, нам нужно добавить текстовое поле, две кнопки и текстовое представление. Посмотрите на следующий скриншот для уточнения. Я также включил атрибуты каждого подпредставления, чтобы помочь вам с этой задачей.
-
UITextField
- Рамка: X = 20, Y = 77, Ширина = 280, Высота = 30
- Текст заполнителя: Угадай число (1 — 100) …
-
UIButton
- Рамка: X = 254, Y = 115, ширина = 46, высота = 30
- Название: Отправить
-
UIButton
- Рамка: X = 20, Y = 115, ширина = 48, высота = 30
- Название: Отмена
-
UITextView
- Рамка: X = 20, Y = 153, ширина = 280, высота = 395
- Текст: Ничего (Удалить существующий текст)
- Цвет фона: светло-серый цвет
- Цвет текста: белый цвет
- Редактируемый: нет
Вот так должен выглядеть контроллер представления после того, как вы добавили четыре подпредставления в его представление.
Шаг 2
Прежде чем мы перейдем к OptionsViewController
, давайте объявим и подключим необходимые выходы и действия в классе ViewController
. Откройте ViewController.h и обновите его содержимое, как показано ниже.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@property (weak, nonatomic) IBOutlet UITextField *txtGuess;
@property (weak, nonatomic) IBOutlet UITextView *tvHistory;
@property (weak, nonatomic) IBOutlet UIButton *btnSend;
@property (weak, nonatomic) IBOutlet UIButton *btnCancel;
— (IBAction)startGame:(id)sender;
— (IBAction)sendGuess:(id)sender;
— (IBAction)cancelGuessing:(id)sender;
@end
|
Пересмотрите основную раскадровку проекта, выберите экземпляр ViewController
и соедините выходы и действия с интерфейсом контроллера представления. Если вы не знакомы с этим шагом, позвольте мне показать вам, как это работает. Начните с расширения сцены контроллера представления, чтобы вы могли видеть связанные с ней объекты.
Следующим шагом является нажатие Ctrl или щелчок правой кнопкой мыши на объекте View Controller — MPCDemo в списке, который должен вызвать черное всплывающее окно. В этом окне вы должны увидеть розетки и действия, которые мы объявили ранее, а также ряд других предметов. Чтобы подключить выход текстового поля, щелкните кружок справа от выхода txtGuess
и перетащите его в текстовое поле в пользовательском интерфейсе. Появляется синяя линия для визуализации связи. Посмотрите на следующий скриншот, чтобы лучше понять процесс.
Прежде чем продолжить, убедитесь, что вы подключили каждую розетку и действие к соответствующему представлению в пользовательском интерфейсе.
Шаг 3
Для класса OptionsViewController
нам нужно добавить новую сцену в интерфейс. Перетащите экземпляр UIViewController
из библиотеки объектов на холст. Чтобы связать его с существующей сценой, нажмите Ctrl или щелкните правой кнопкой мыши на элементе панели, обозначенном Options, и перетащите его в новый контроллер вида. В появившемся черном всплывающем окне выберите Push из списка параметров.
Имя класса новой сцены по умолчанию — UIViewController
, но это то, что мы хотим изменить. В нижней части второй сцены выберите первый элемент, откройте Identity Inspector справа и установите для поля Class значение OptionsViewController
в разделе Custom Class .
Выберите панель навигации контроллера представления параметров и установите заголовок « Параметры» в Инспекторе атрибутов . Добавьте пять подпредставлений к представлению контроллера представления, как показано ниже.
-
UITextField
- Рамка: X = 20, Y = 77, Ширина = 280, Высота = 30
- Текст заполнителя: Ваше имя игрока …
-
UISwitch
- Рамка: X = 136, Y = 141, ширина = 51, высота = 31
-
UIButton
- Рамка: X = 76, Y = 231, ширина = 168, высота = 30
- Название: Обзор для других игроков
-
UITextView
- Рамка: X = 20, Y = 269, ширина = 280, высота = 241
- Текст: Ничего (Удалить существующее содержимое)
- Редактируемый: НЕТ
-
UIButton
- Рамка: X = 121, Y = 518, ширина = 78, высота = 30
- Название: Отключить
Следующий скриншот должен дать вам представление о том, как должен выглядеть конечный результат.
Шаг 4
Чтобы завершить пользовательский интерфейс контроллера представления параметров, давайте объявим некоторые выходы и действия в заголовочном файле контроллера представления. Откройте OptionsViewController.h и обновите его содержимое, как показано ниже.
01
02
03
04
05
06
07
08
09
10
11
12
13
|
#import <UIKit/UIKit.h>
@interface OptionsViewController : UIViewController
@property (weak, nonatomic) IBOutlet UITextField *txtPlayerName;
@property (weak, nonatomic) IBOutlet UISwitch *swVisible;
@property (weak, nonatomic) IBOutlet UITextView *tvPlayerList;
— (IBAction)disconnect:(id)sender;
— (IBAction)searchForPlayers:(id)sender;
— (IBAction)toggleVisibility:(id)sender;
@end
|
Пересмотрите основную раскадровку проекта и соедините выходы и действия с пользовательским интерфейсом. Мы завершили пользовательский интерфейс игры и готовы приступить к написанию некоторого кода, чтобы увидеть платформу Multipeer Connectivity в действии.
7. Обнаружение других игроков
Следующим шагом является реализация класса OptionsViewController
, потому что нам нужно соединение между двумя устройствами, прежде чем мы сможем играть в игру и сосредоточиться на логике игры. Я хотел бы начать с показа вам, как обнаружить соседних игроков (устройств) с помощью встроенного решения Multipeer Connectivity Framework.
Шаг 1
Первым шагом является получение объекта mpcHandler
мы создали ранее в классе AppDelegate
. Это подразумевает, что нам нужен доступ к объекту mpcHandler
через делегат приложения. Откройте OptionsViewController.m, перейдите к началу файла и добавьте оператор импорта для файла AppDelegate
класса AppDelegate
.
1
2
3
4
5
6
7
|
#import «OptionsViewController.h»
#import «AppDelegate.h»
@interface OptionsViewController ()
@end
|
В расширении класса объявите свойство, которое будет хранить ссылку на экземпляр AppDelegate
.
1
2
3
4
5
6
7
8
9
|
#import «OptionsViewController.h»
#import «AppDelegate.h»
@interface OptionsViewController ()
@property (strong, nonatomic) AppDelegate *appDelegate;
@end
|
Нет необходимости создавать экземпляр экземпляра AppDelegate
поскольку это автоматически выполняется во время запуска приложения. В методе viewDidLoad
контроллера viewDidLoad
мы получаем ссылку на делегат приложения и настраиваем объект mpcHandler
. Мы инициализируем peerID
и свойства MPCHandler
класса MPCHandler
, вызывая методы, которые мы объявили и реализовали ранее в этом руководстве.
1
2
3
4
5
6
7
8
9
|
— (void)viewDidLoad {
[super viewDidLoad];
self.appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
[self.appDelegate.mpcHandler setupPeerWithDisplayName:[UIDevice currentDevice].name];
[self.appDelegate.mpcHandler setupSession];
[self.appDelegate.mpcHandler advertiseSelf:self.swVisible.isOn];
}
|
В viewDidLoad
мы настраиваем одноранговый viewDidLoad
, передавая имя устройства в качестве отображаемого имени. Мы также инициализируем свойство MPCHandler
экземпляра MPCHandler
которое мы будем использовать позже для установления соединения между узлами. Важно, чтобы мы сделали это после инициализации экземпляра MCPeerID
, потому что сеанс требует допустимого объекта peerID
во время его инициализации. Наконец, мы вызываем advertiseSelf:
и передаем состояние коммутатора в пользовательский интерфейс контроллера представления параметров. Состояние по умолчанию коммутатора — YES
, что означает, что наше устройство может быть обнаружено другими устройствами.
Шаг 2
Представить контроллер представления браузера так же просто, как и получается. Однако свойство MPCHandler
экземпляра MPCHandler
необходимо сначала инициализировать. Мы хотим показать контроллер представления браузера, когда пользователь нажимает кнопку « Обзор других игроков» . Давайте реализуем searchForPlayers:
действие дальше.
1
2
3
4
5
6
7
8
9
|
— (IBAction)searchForPlayers:(id)sender {
if (self.appDelegate.mpcHandler.session != nil) {
[[self.appDelegate mpcHandler] setupBrowser];
[self presentViewController:self.appDelegate.mpcHandler.browser
animated:YES
completion:nil];
}
}
|
Представление контроллера представления браузера требует двух шагов. Сначала мы вызываем setupBrowser
для объекта mpcHandler
делегата приложения, чтобы инициализировать экземпляр MCBrowserViewController
а затем представляем этот экземпляр пользователю. Обратите внимание, что сначала мы проверяем, не равен ли объект MPCHandler
экземпляра MPCHandler
nil
, поскольку объект session
используется для инициализации экземпляра MCBrowserViewController
.
Если вы запустите приложение и выполните поиск других игроков, вы должны увидеть вид, аналогичный показанному на следующем снимке экрана.
Чтобы протестировать приложение, вам нужно как минимум два устройства, например, iOS Simulator и физическое устройство. Чтобы создать скриншот выше, я запустил приложение в iOS Simulator и на устройстве. Если вы нажмете на имя устройства, выбранное устройство будет перемещено в раздел с надписью « Приглашенные», как показано ниже.
В то же время на втором устройстве отображается окно с предупреждением, уведомляющее другого игрока о том, что вы хотите установить соединение. Приглашенный может принять или отклонить приглашение. Если игрок принимает приглашение, браузер устанавливает соединение между двумя устройствами.
Когда второй игрок принимает приглашение, кнопка « Готово» на контроллере представления браузера активируется. Однако, если вы попытаетесь нажать кнопки « Отмена» или « Готово» , вы заметите, что ничего не происходит. Почему это? Объяснение простое, сначала нам нужно реализовать протокол OptionsViewController
классе OptionsViewController
. Давайте сделаем это сейчас. Однако сначала обновите действие searchForPlayers:
установив контроллер представления параметров в качестве делегата контроллера представления браузера.
01
02
03
04
05
06
07
08
09
10
|
— (IBAction)searchForPlayers:(id)sender {
if (self.appDelegate.mpcHandler.session != nil) {
[[self.appDelegate mpcHandler] setupBrowser];
[[[self.appDelegate mpcHandler] browser] setDelegate:self];
[self presentViewController:self.appDelegate.mpcHandler.browser
animated:YES
completion:nil];
}
}
|
Однако это приводит к другому предупреждению компилятора, поскольку OptionsViewController
еще не соответствует протоколу MCBrowserViewControllerDelegate
. Чтобы это исправить, откройте OptionsViewController.h, импортируйте файлы заголовков платформы Multipeer Connectivity и OptionsViewController
соответствие с протоколом MCBrowserViewControllerDelegate
.
1
2
|
#import <UIKit/UIKit.h>
#import <MultipeerConnectivity/MultipeerConnectivity.h>
|
01
02
03
04
05
06
07
08
09
10
11
|
@interface OptionsViewController : UIViewController <MCBrowserViewControllerDelegate>
@property (weak, nonatomic) IBOutlet UITextField *txtPlayerName;
@property (weak, nonatomic) IBOutlet UISwitch *swVisible;
@property (weak, nonatomic) IBOutlet UITextView *tvPlayerList;
— (IBAction)disconnect:(id)sender;
— (IBAction)searchForPlayers:(id)sender;
— (IBAction)toggleVisibility:(id)sender;
@end
|
Нам нужно реализовать два метода протокола MCBrowserViewControllerDelegate
, чтобы включить кнопки « Отмена» и « Done
. Реализации идентичны, то есть отклонение контроллера представления браузера. Откройте файл OptionsViewController
класса OptionsViewController
и добавьте два метода делегата, как показано ниже.
1
2
3
4
5
6
7
|
— (void)browserViewControllerDidFinish:(MCBrowserViewController *)browserViewController {
[self.appDelegate.mpcHandler.browser dismissViewControllerAnimated:YES completion:nil];
}
— (void)browserViewControllerWasCancelled:(MCBrowserViewController *)browserViewController {
[self.appDelegate.mpcHandler.browser dismissViewControllerAnimated:YES completion:nil];
}
|
Если вы снова запустите приложение, вы увидите, что кнопки « Отмена» и « Готово» работают должным образом. При нажатии на кнопку « Готово» устанавливается скрытое соединение, а контроллер представления браузера отключается.
8. Обнаружение изменений состояния соединения
Предыдущий раздел был ключевым аспектом структуры Multipeer Connectivity. Однако после нажатия кнопки « Готово» мы не заметили никаких изменений. Это почему? В процессе подключения устройства, которые подключаются, переходят из состояния « Не подключено» в состояние « Соединение» и, если все идет хорошо, переходят в состояние « Подключено» . Пока эти переходы имеют место, session:peer:didChangeState:
метод session:peer:didChangeState:
делегат платформы Multipeer Connectivity. В результате настраиваемое уведомление, которое мы настраиваем в классе MPCHandler
публикуется при каждом изменении состояния.
Наша задача — наблюдать за этими уведомлениями и соответствующим образом реагировать, когда одноранговый узел подключен или отключен от устройства пользователя. Всякий раз, когда состояние соединения изменяется, кроме состояния Соединения , мы обновляем текстовое представление класса OptionsViewController
игроками, которые в данный момент подключены к устройству пользователя.
Во-первых, мы должны добавить экземпляр контроллера представления в качестве наблюдателя для уведомлений MPCDemo_DidChangeStateNotification
. Обновите метод viewDidLoad
контроллера представления, как показано ниже.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
— (void)viewDidLoad {
[super viewDidLoad];
self.appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
[self.appDelegate.mpcHandler setupPeerWithDisplayName:[UIDevice currentDevice].name];
[self.appDelegate.mpcHandler setupSession];
[self.appDelegate.mpcHandler advertiseSelf:self.swVisible.isOn];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(peerChangedStateWithNotification:)
name:@»MPCDemo_DidChangeStateNotification»
object:nil];
}
|
Помните, что уведомление с именем MPCDemo_DidChangeStateNotification
публикуется каждый раз, когда изменяется состояние соединения однорангового MPCDemo_DidChangeStateNotification
. Когда это происходит, вызывается peerChangedStateWithNotification:
контроллера представления. Реализация peerChangedStateWithNotification:
не сложно, как вы можете видеть ниже. Мы извлекаем состояние однорангового userInfo
словаря userInfo
уведомления и, если оно не равно MCSessionStateConnecting
, мы обновляем текстовое представление контроллера представления для отображения состояний каждого однорангового MCSessionStateConnecting
.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
— (void)peerChangedStateWithNotification:(NSNotification *)notification {
// Get the state of the peer.
int state = [[[notification userInfo] objectForKey:@»state»] intValue];
// We care only for the Connected and the Not Connected states.
// The Connecting state will be simply ignored.
if (state != MCSessionStateConnecting) {
// We’ll just display all the connected peers (players) to the text view.
NSString *allPlayers = @»Other players connected with:\n\n»;
for (int i = 0; i < self.appDelegate.mpcHandler.session.connectedPeers.count; i++) {
NSString *displayName = [[self.appDelegate.mpcHandler.session.connectedPeers objectAtIndex:i] displayName];
allPlayers = [allPlayers stringByAppendingString:@»\n»];
allPlayers = [allPlayers stringByAppendingString:displayName];
}
[self.tvPlayerList setText:allPlayers];
}
}
|
Как следует из его названия, свойство connectedPeers
объекта session
является массивом, который содержит подключенные узлы. Мы используем цикл for
чтобы перебрать этот массив и заполнить текстовое представление именами связанных пиров.
Запустите два экземпляра приложения и установите соединение, чтобы увидеть результат нашей работы. Теперь, когда вы нажмете кнопку « Готово» , в текстовом представлении отобразится список пиров, подключенных к устройству пользователя.
Вывод
Это завершает его для первой части этого руководства о структуре многопользовательского подключения. Мы рассмотрели несколько новых API и заложили основы нашей игры. В следующем уроке мы продолжим создание игры путем реализации логики игры, а также интеграции инфраструктуры Multipeer Connectivity для передачи данных между игроками в игре.