В этом руководстве будет использован проект с открытым исходным кодом Leaves для создания простого устройства чтения iPad для «Войны миров » Х.Г. Уэллса. Попутно мы кратко рассмотрим код, обеспечивающий реализацию проекта Leaves, обсудим детали реализации и рассмотрим несколько альтернативных вариантов достижения аналогичного эффекта.
О Проекте Листьев
Leaves — это компонент iOS с открытым исходным кодом, который имитирует переход между страницами просмотра при переворачивании страниц, как и в официальном приложении Apple iBooks. Первоначально проект был написан Томом Брауом и доступен на GitHub , где позднее он был разветвлен Оле Бегеманом для добавления поддержки многостраничных макетов.
Чтобы увидеть проект Leaves в действии, взгляните на следующую видео-демонстрацию проекта, который научит вас создавать этот учебник:
Конечно, продемонстрированный выше эффект скручивания страницы не уникален ни для этой демонстрации, ни для приложения iBooks. Другие родные приложения для iOS (например, приложение «Карты») также используют аналогичный эффект. Чтобы получить полную предысторию о том, как именно Apple достигает этого в своих приложениях и почему разработчикам iOS SDK нужно прибегнуть к такому компоненту, как Leaves, ознакомьтесь с «Анимациями скручивания страниц в App Store» от Ole Begemann и «Apple iBooks Dynamic Page Curl» от Стивен Трутон-Смит.
Позже мы вернемся к некоторой теории, лежащей в основе проекта Leaves, но давайте продолжим и перейдем к созданию нашего читателя « Войны миров», чтобы понять, как код выглядит в действии.
Сборка WOTW PDF Reader
Шаг 1: Загрузите ресурсы проекта
Загрузочный файл, прикрепленный к этому сообщению Mobiletuts +, содержит исходный код Leaves и несколько общедоступных ресурсов (включая текст « Войны миров» ), использованных в проекте.
Шаг 2: Создайте новый проект Xcode
Откройте Xcode и создайте новый проект, используя шаблон «View-based Application». Назовите проект «WOTW» и выберите «iPad» в раскрывающемся списке семейства устройств.
Шаг 3: добавьте ресурсы проекта в Xcode
Перетащите следующие файлы в группу «Вспомогательные файлы» в XCode:
- WOTW.pdf
- Icon.png
- [email protected]
Все перечисленные выше файлы можно найти в папке «Ресурсы», прилагаемой к данному руководству.
Затем добавьте фактический код компонента Leaves. Создайте новую группу под названием «Листья» в папке «WOTW» в навигаторе проекта XCode, а затем перетащите следующие файлы в группу «Листья»:
- Utilities.h
- Utilities.m
- LeavesCache.h
- LeavesCache.m
- LeavesView.h
- LeavesView.m
- LeavesViewController.h
- LeavesViewController.m
ПРИМЕЧАНИЕ. Обязательно установите флажок «Копировать объекты в папку целевой группы» при добавлении вышеуказанных файлов.
Шаг 4: Связать платформу QuartzCore
Анимация, созданная Leaves, зависит от QuartzCore Framework. Следовательно, вам нужно будет связать эту структуру с вашим проектом, чтобы Leaves работали. Чтобы сделать это в Xcode 4, начните с выбора «WOTW» в Project Navigator. Затем выберите цель «WOTW», а затем вкладку «Фазы сборки». Затем разверните раскрывающийся список «Связать двоичные файлы с библиотеками» и щелкните значок «плюс», чтобы добавить новую платформу. Наконец, выберите «QuartzCore» из списка доступных платформ и нажмите «Добавить». Результат этого процесса визуально отображается ниже:
Шаг 5: Подкласс LeavesViewController
Для нашего проекта мы хотим, чтобы читатель немедленно запустил текст « Войны миров» без дополнительной страницы. Для этого нам нужно сделать так, WOTWViewController
класс WOTWViewController
наследовался от класса LeavesViewController
а не от UIViewController
. Сделайте это, открыв WOTWViewController.h и изменив объявление интерфейса следующим образом:
1
2
3
|
@interface WOTWViewController : LeavesViewController {
}
|
Строка 1 выше изменяет WOTWViewController
для наследования от LeavesViewController
а не напрямую от UIViewController
. Поскольку сам LeavesViewController
наследуется от UIViewController
, вы можете думать о WOTWViewController
как о UIViewController
Итак, что мы получаем за нашу проблему? LeavesViewController
соответствует источнику данных Leaves и протоколам делегатов (обсуждается позже) и определяет пользовательские реализации -loadView
и -viewDidLoad
которые добавляют специальный вид UIView
называемый LeavesView
к текущей иерархии представления. Класс LeavesView
отвечает за большую часть работы за анимацию скручивания страницы.
Шаг 6: Добавьте необходимые импортные операторы
Чтобы класс WOTWViewController
мог наследоваться от класса LeavesViewController
, нам необходимо импортировать код LeavesViewController
. Добавьте следующие две строки #import
в WOTWViewController.h :
1
2
|
#import «Utilities.h»
#import «LeavesViewController.h»
|
Хотите знать, для чего предназначен этот оператор импорта Utilities.h
? Как мы увидим позже, проект Leaves опирается на умную функцию, объявленную в этом файле под названием aspectFit
. Многие проекты Cocoa-Touch поддерживают класс Utilities для объявления и определения вспомогательного кода, используемого в приложении.
Шаг 7: Объявление членов данных WOTW
На данный момент нам нужно всего лишь объявить один элемент данных для нашего проекта. В файле WOTWViewController.h добавьте следующую строку кода:
1
|
CGPDFDocumentRef bookPDF;
|
Мы будем использовать «bookPDF» в качестве ссылки на файл WOTW.pdf позже. CGPDFDocumentRef
переменной CGPDFDocumentRef
будет широко использоваться в этом учебном пособии и заслуживает дальнейшего изучения в официальной документации Apple .
Шаг 8: инициализировать листья
Когда WOTWViewController
создан, нам нужно инициализировать bookPDF
данных bookPDF
объявленный выше. Как показано в примере проекта Leaves, вы можете сделать это с помощью следующих строк кода:
1
2
3
4
5
6
7
8
|
— (id)init {
if (self = [super init]) {
CFURLRef pdfURL = CFBundleCopyResourceURL(CFBundleGetMainBundle(), CFSTR(«WOTW.pdf»), NULL, NULL);
bookPDF = CGPDFDocumentCreateWithURL((CFURLRef)pdfURL);
CFRelease(pdfURL);
}
return self;
}
|
Вы видите проблему с подходом выше? Помните, что мы собираемся загружать наш контроллер представления из Интерфейсного Разработчика.
Пример проекта Leaves предполагает, что вы вручную LeavesViewController
экземпляр LeavesViewController
программным способом, вызвав метод -(id)init
, например:
1
|
WOTWViewController *viewController = [[[WOTWViewController alloc] init];
|
Однако в нашем проекте мы хотим разархивировать WOTWViewController
из нашего интерфейса Interface Builder, поэтому только что реализованная нами функция -(id)init
никогда не будет вызываться. Вместо этого нам нужно предоставить пользовательскую реализацию для -(id)initWithCoder:
метод, чтобы подключиться к процессу разархивирования NIB и выполнить пользовательскую инициализацию. Хорошо оставить init
на месте, чтобы у вас была возможность создать этот контроллер представления вручную, но мы должны создать новый метод для инициализации PDF и вызвать этот метод из обоих -(id)init
и -(id)initWithCoder:
Добавьте следующую строку кода в файл WOTWViewController.h :
- (недействительная) loadPDF;
Затем переключитесь на WOTWViewController.m и реализуйте метод следующим образом:
1
2
3
4
5
6
|
-(void)loadPDF
{
CFURLRef pdfURL = CFBundleCopyResourceURL(CFBundleGetMainBundle(), CFSTR(«WOTW.pdf»), NULL, NULL);
bookPDF = CGPDFDocumentCreateWithURL((CFURLRef)pdfURL);
CFRelease(pdfURL);
}
|
Наконец, вызовите метод loadPDF
из инициализаторов класса:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
— (id)init {
self = [super init];
if (self) {
[self loadPDF];
}
return self;
}
-(id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self)
{
[self loadPDF];
}
return self;
}
|
Когда наш пользовательский контроллер представления разархивирован, будет вызван метод -(id)initWithCoder:
и затем будет loadPDF
метод loadPDF
.
Есть еще одна вещь. Выделив память для ссылки на bookPDF
, мы также должны освободить эту память, когда закончим с ней. Мы можем сделать это, добавив следующую строку кода в метод -(void)dealloc:
::
1
|
CGPDFDocumentRelease(bookPDF);
|
Шаг 9: Реализация источника данных Leaves
Напомним из шага 5, что класс LeavesViewController
автоматически добавляет экземпляр класса LeavesView
в иерархию представления. Класс LeavesView
каким-то образом должен выяснить, какой контент должен отображаться в представлении, и это достигается путем вызова двух пользовательских методов источника данных: -(NSUInteger)numberOfPagesInLeavesView:
и -(void)renderPageAtIndex:
Чтобы обеспечить пользовательскую реализацию для них, откройте WOTWViewController.m и добавьте следующие строки кода:
01
02
03
04
05
06
07
08
09
10
11
12
13
|
#pragma mark LeavesViewDataSource methods
— (NSUInteger) numberOfPagesInLeavesView:(LeavesView*)leavesView {
return CGPDFDocumentGetNumberOfPages(bookPDF);
}
— (void) renderPageAtIndex:(NSUInteger)index inContext:(CGContextRef)ctx {
CGPDFPageRef page = CGPDFDocumentGetPage(bookPDF, index + 1);
CGAffineTransform transform = aspectFit(CGPDFPageGetBoxRect(page, kCGPDFMediaBox),
CGContextGetClipBoundingBox(ctx));
CGContextConcatCTM(ctx, transform);
CGContextDrawPDFPage(ctx, page);
}
|
Метод -(NSUInteger)numberOfPagesInLeavesView:
просто указывает, сколько страниц содержимого LeavesViewController
отвечает за отображение. Если вы создавали контент для Leaves вручную или точно знали, сколько страниц в PDF, вы можете вернуть это число вручную здесь. Конечно, всегда лучше определить это число динамически, и именно это CGPDFDocumentGetNumberOfPages()
функция CGPDFDocumentGetNumberOfPages()
.
Метод -(void)renderPageAtIndex:inContext:
отвечает за фактическое рисование CGContextRef
после первого добавления содержимого PDF в контекст, переданный как ctx
.
Тестирование кода
Постройте и запустите проект. Если все идет хорошо, теперь вы сможете увидеть обложку книги « Война миров» и пролистать страницы с анимацией скручивания страниц!
Окончательный файл WOTWViewController.h должен выглядеть следующим образом:
01
02
03
04
05
06
07
08
09
10
11
12
|
#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>
#import «Utilities.h»
#import «LeavesViewController.h»
@interface WOTWViewController : LeavesViewController {
CGPDFDocumentRef bookPDF;
}
-(void)loadPDF;
@end
|
И окончательный файл WOTWViewController.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
|
#import «WOTWViewController.h»
@implementation WOTWViewController
#pragma mark — Initialization / Memory Management
— (id)init {
self = [super init];
if (self) {
[self loadPDF];
}
return self;
}
-(id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self)
{
[self loadPDF];
}
return self;
}
-(void)loadPDF
{
CFURLRef pdfURL = CFBundleCopyResourceURL(CFBundleGetMainBundle(), CFSTR(«WOTW.pdf»), NULL, NULL);
bookPDF = CGPDFDocumentCreateWithURL((CFURLRef)pdfURL);
CFRelease(pdfURL);
}
— (void)dealloc
{
CGPDFDocumentRelease(bookPDF);
[super dealloc];
}
#pragma mark — LeavesViewDataSource methods
— (NSUInteger) numberOfPagesInLeavesView:(LeavesView*)leavesView {
return CGPDFDocumentGetNumberOfPages(bookPDF);
}
— (void) renderPageAtIndex:(NSUInteger)index inContext:(CGContextRef)ctx {
CGPDFPageRef page = CGPDFDocumentGetPage(bookPDF, index + 1);
CGAffineTransform transform = aspectFit(CGPDFPageGetBoxRect(page, kCGPDFMediaBox),
CGContextGetClipBoundingBox(ctx));
CGContextConcatCTM(ctx, transform);
CGContextDrawPDFPage(ctx, page);
}
@end
|
Альтернативы Листьям
В своем исследовании Leaves я наткнулся на несколько дополнительных проектов с открытым исходным кодом, в которых добавлена анимация перехода в виде страницы. Если вы нашли этот проект интересным, вам также следует проверить:
- FlipView — FlipBook как анимация
- HMGLTransitions — развороты страниц OpenGL
- Paperstack — OpenGL (не выпущено 30.08.2011 — надеюсь, скоро!)
Оставьте комментарий ниже, если вы хотите увидеть учебник по одному из вышеперечисленных проектов!
Хочешь увидеть больше?
Мы достигли определенного прогресса в этом уроке. Теперь мы можем взаимодействовать с « Войной миров» в формате PDF, а анимация скручивания страницы добавляет эстетическое чувство к электронной книге. Однако предстоит еще много работы, прежде чем это приложение будет готово к выпуску в App Store. Например, было бы замечательно, если бы пользователь автоматически возвращался к последней странице, к которой он обращался при запуске приложения, и было бы очень хорошо иметь UISlider
или подобный компонент интерфейса для быстрого UISlider
между разделами книги. Другие полезные функции могут включать поиск, выделение текста, оглавление или закладки.
Я хочу убедиться, что мои учебники по iOS SDK охватывают темы, в которых заинтересовано сообщество Mobiletuts +. Как вы думаете, мне следует создать полноценный eReader и поделиться своим исходным кодом? Или, возможно, вы хотели бы увидеть учебник по чему-то совершенно другому? Ответьте на следующий опрос и дайте мне знать!
ОБНОВЛЕНИЕ 7/7/2011: опрос закрыт. Более 60% респондентов выразили заинтересованность в том, чтобы увидеть больше постов о добавлении оглавления и / или UISlider, а также было опубликовано руководство с использованием UISlider (ссылка ниже).
Вы также можете отправить свой отзыв на мою учетную запись Twitter ( @markhammonds ), хотя я признаю, что обычно использую Twitter только тогда, когда не работаю над внештатными проектами или не пишу учебные пособия, поэтому вы можете просто вместо этого связаться со мной по электронной почте .