Статьи

Цифровые билеты с UIGestureRecognizer

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


UIGestureRecognizer и его различные подклассы позволяют легко распознавать различные жесты на назначенных целевых объектах и ​​выполнять действия, когда эти жесты распознаются. Можно комбинировать различные UIGestureRecognizers чтобы заставить людей делать нетривиальные жесты для выполнения действия. Это может быть использовано в тех случаях, когда пользователи пытаются инициировать необратимое действие, когда простой, подверженный несчастным случаям жест, такой как касание или пролистывание, не подходит.

Билеты указывают на право допуска на различные мероприятия или места. Они обычно перфорированы, чтобы показать, что билет был использован и больше не подлежит обмену. Apple представила приложение Passbook, которое позволяет людям использовать свой телефон в качестве билета или другой формы мобильного платежа. Passbook ограничен представлением билетов с QR-кодом в качестве единственного метода их погашения.

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

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


Откройте Xcode и выберите «Создать новый проект Xcode». Выберите «Очистить приложение» и нажмите «Далее». Введите название для вашего проекта, я назвал мои «Билеты». Введите название своей организации, идентификатор компании и префикс класса. Выберите iPhone из устройств и просто выберите «Использовать автоматический подсчет ссылок». Нам не нужны юнит-тесты или базовые данные для этого проекта.

цифровые билеты создать-проект

Этот проект будет иметь два контроллера представления, один экран для представления списка всех доступных билетов и один экран, где билеты фактически выкуплены.

Создайте подкласс UITableViewController под названием «TicketListViewController». Этот контроллер не требует XIB для пользовательского интерфейса, поскольку это просто список.

цифровые билеты создать-билет-лист-представление-контроллер

Создайте подкласс UIViewController под названием «RedeemTicketViewController». Выберите «С XIB для пользовательского интерфейса», поскольку этот экран будет иметь более сложный дизайн.

цифровые билеты создать-искупит-билеты-представление-контроллер

Теперь создайте навигацию через приложение. В верхней части вашего приложения делегат импортирует TicketListViewController.h .

1
#import “TicketListViewController.h”

В application:didFinishLaunchingWithOptions: создайте экземпляр контроллера представления списка application:didFinishLaunchingWithOptions: и экземпляр UINavigationController с контроллером представления списка application:didFinishLaunchingWithOptions: качестве корневого контроллера представления. Установите корневой контроллер окна в качестве контроллера навигации.

01
02
03
04
05
06
07
08
09
10
11
— (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
 
    TicketListViewController* vc = [[TicketListViewController alloc] initWithStyle:UITableViewStylePlain];
    UINavigationController* navController = [[UINavigationController alloc] initWithRootViewController:vc];
    [self.window setRootViewController:navController];
 
    [self.window makeKeyAndVisible];
    return YES;
}

Создайте и запустите приложение, чтобы убедиться, что все правильно подключено. Вы должны увидеть пустой экран на экране.


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

Отредактируйте метод init в TicketListViewController чтобы установить заголовок панели навигации для этого экрана. Это поможет пользователям ориентироваться в приложении.

01
02
03
04
05
06
07
08
09
10
— (id)initWithStyle:(UITableViewStyle)style
{
    self = [super initWithStyle:style];
    if (self) {
 
        self.navigationItem.title = @»Tickets»;
 
    }
    return self;
}

Измените источник данных табличного представления, чтобы иметь один раздел и одну строку. Создайте ячейку со стилем UITableViewCellStyleSubtitle . Установите текстовую метку для отображения имени события и места, текстовую метку для отображения даты события и тип аксессуара UITableViewCellAccessoryDisclosureIndicator .

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
#pragma mark — Table view data source
 
— (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}
 
— (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return 1;
}
 
— (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @»Cell»;
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if(!cell){
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
    }
 
    cell.textLabel.text = @»Special Guests at Generic Venue»;
    cell.detailTextLabel.text = @»19 Jun 2013″;
    cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
 
    return cell;
}

В верхней части TicketListViewController.m импортируйте RedeemTickerViewController.h .

1
#import “RedeemTickerViewController.h”

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

1
2
3
4
5
6
7
8
9
#pragma mark — Table view delegate
 
— (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
 
    RedeemTicketViewController* vc = [[RedeemTicketViewController alloc]initWithNibName:@»RedeemTicketViewController» bundle:nil];
    [self.navigationController pushViewController:vc animated:YES];
}

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

цифровые билеты билет-лист предварительного просмотра

Это самый интересный экран с большей интерактивностью и функциональностью.

В RedeemTickerViewController.h создайте выход для заглушки билета UIImageView , основной части билета UIImageView и UIView который позволит нам анимировать билет, как будто он отрывается. BOOL переменную экземпляра BOOL которую мы будем использовать для отслеживания того, удерживается ли квитанция на месте. Создайте UILongPressGestureRecognizer и UISwipeGestureRecognizer который мы будем использовать позже.

Файл заголовка теперь должен быть таким:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
#import <UIKit/UIKit.h>
 
@interface RedeemTicketViewController : UIViewController
 
@property (nonatomic, strong) IBOutlet UIView* containerView;
@property (nonatomic, strong) IBOutlet UIImageView* ticketStub;
@property (nonatomic, strong) IBOutlet UIImageView* ticketMain;
 
@property (nonatomic, strong) UILongPressGestureRecognizer* longPressGest;
@property (nonatomic, strong) UISwipeGestureRecognizer* swipeGest;
 
@property (nonatomic, assign) BOOL isHeld;
 
@end

Откройте RedeemTicketViewController.xib и выберите объект представления контроллера представления. Снимите флажок «Использовать Autolayout» на панели «Инспектор файлов», чтобы включить совместимость до iOS6. Мы установим маску авторазмера для каждого вида сами, чтобы гарантировать, что теперь мы учитываем различные размеры экрана на iPhone.

Теперь добавьте представление изображения из библиотеки объектов. Это будет для заглушки ticketStub , поэтому подключите выходной ticketStub владельца файла ticketStub к представлению изображения. Установите размер рамки вида изображения, чтобы быть в начале (20, 20) и иметь размер (280, 100) . Установите маску автоматического изменения размера, чтобы иметь фиксированное верхнее поле и не изменять размеры по ширине или высоте.

цифровые билеты создать билет окурок ракурс

Теперь добавьте вид из библиотеки объектов. Это будет представление контейнера, поэтому подключите к представлению выход containerView владельца файла. Установите рамку вида на (20, 120) и размером (280, 280) . Установите маску автоматического изменения размера, чтобы иметь фиксированное верхнее поле и не изменять размеры по ширине или высоте.

цифровые билеты создать билет магистральный-контейнер-вид

Затем перетащите представление изображений из библиотеки объектов в только что созданный вид контейнера. Представление изображения должно заполнять представление контейнера и иметь его маску авторазмера, чтобы все поля были фиксированными, а также для изменения ширины и высоты. Установите выходной ticketMain владельца файла на представление изображения.

цифровые билеты создать билет-главный ракурс

В инспекторе атрибутов выберите изображения для заглушки и основные части заявки, используя изображения, которые вы импортировали в свой проект. Убедитесь, что вы отметили «Взаимодействие с пользователем включено» как для представлений изображения, так и для представления контейнера.

Отредактируйте метод init в RedeemTicketViewController чтобы задать заголовок панели навигации для этого экрана и инициализируйте self.isHeld значением NO .

1
2
3
4
5
6
7
8
9
— (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        self.navigationItem.title = @»Special Guests at Generic Venue»;
        self.isHeld = NO;
    }
    return self;
}

Создайте и запустите приложение, чтобы проверить, как оно выглядит.

цифровые билеты, выкупить билет предварительного просмотра

Я включил QR-код в свои изображения, чтобы, если бы системы билетов на месте были обновлены для обработки Passbook, было бы беспрепятственно продолжать использовать это приложение после обновления.


В viewDidLoad ( viewDidLoad после загрузки представления из XIB) создайте UILongPressGestureRecognizer и добавьте его в ticketStub изображения ticketStub . Создайте UISwipeGestureRecognizer и добавьте его в представление изображения UISwipeGestureRecognizer .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
-(void)viewDidLoad
{
    [super viewDidLoad];
 
    self.longPressGest = [[UILongPressGestureRecognizer alloc]
                          initWithTarget:self action:@selector(handleLongPress:)];
    self.longPressGest.minimumPressDuration = 0.1;
    [self.ticketStub addGestureRecognizer:self.longPressGest];
 
    self.swipeGest = [[UISwipeGestureRecognizer alloc]
                      initWithTarget:self action:@selector(handleSwipe:)];
    self.swipeGest.direction = (UISwipeGestureRecognizerDirectionLeft | UISwipeGestureRecognizerDirectionRight);
    [self.ticketMain addGestureRecognizer:self.swipeGest];
}

Обратите внимание, что я установил маску направления в распознавателе жестов смахивания для обработки влево или вправо, а не вверх или вниз. Я установил минимальную продолжительность печати 0,1 секунды. Их можно настроить в вашем приложении, чтобы создать максимально реалистичное действие.

Затем напишите методы для обработки длинного нажатия и прокрутки, называемые handleLongPress: и handleSwipe: соответственно. Метод длинного нажатия будет использоваться для определения, действительно ли жест смахивания имеет какой-либо эффект. Представьте себе физический лист бумаги. Вы можете попытаться разорвать его столько, сколько захотите, но если он не удерживается на месте, то ничего не произойдет.

handleLongPress: вызывается всякий раз, когда изменяется состояние распознавателя жестов, например, начинается или заканчивается жест. Мы хотим проверить, закончился ли жест, т. self.isHeld Поднял палец, и убедиться, что self.isHeld снова установлен в NO . Если, если жест не закончился, установите для self.isHeld значение YES которое мы будем использовать, чтобы смахивание выполняло свое действие.

1
2
3
4
5
6
7
8
— (void)handleLongPress:(UIGestureRecognizer *)sender
{
    if (sender.state == UIGestureRecognizerStateEnded) {
        self.isHeld = NO;
    } else if(!self.isHeld){
        self.isHeld = YES;
    }
}

Далее давайте реализуем handleSwipe: Мы проверяем, имеет ли self.isHeld true, и, если это так, мы self.isHeld представление self.isHeld с экрана, используя переход в виде свернувшись вверх, который выглядит так, будто билет разрывается.

1
2
3
4
5
6
7
8
9
— (void)handleSwipe:(UIGestureRecognizer *)sender
{
    if(self.isHeld){
        [UIView transitionWithView:self.containerView duration:1
                           options:UIViewAnimationOptionTransitionCurlUp
                        animations:^ { [sender.view removeFromSuperview];
                        completion:nil];
    }
}

Создайте и запустите приложение, чтобы опробовать новые жесты. Нажмите и удерживайте квитанцию ​​билета и одновременно проведите по другой части билета. Вы должны увидеть основную часть «оторвать» и исчезнуть.

цифровые билеты, выкупить билет-анимации предварительного просмотра

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

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

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

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