Questo tutorial utilizzerà il progetto open source di Leaves per creare un semplice lettore per iPad La guerra dei mondi di H.G. Wells. Lungo la strada, daremo un'occhiata al codice che alimenta il progetto Leaves, discuteremo i dettagli di implementazione ed esploreremo alcune opzioni alternative per ottenere un effetto simile.
Leaves è un componente iOS open-source che simula una transizione di pagina tra le visualizzazioni del contenuto, in modo molto simile alla transizione trovata nell'applicazione iBooks ufficiale di Apple. Il progetto è stato originariamente scritto da Tom Brow e reso disponibile su GitHub, dove è stato successivamente biforcato da Ole Begemann per aggiungere il supporto per i layout multi-pagina.
Per vedere il progetto Leaves in azione, dai un'occhiata alla seguente demo video del progetto che questo tutorial ti insegnerà a creare:
Ovviamente, l'effetto di curling della pagina mostrato sopra non è univoco né per questa demo né per l'applicazione iBooks. Anche altre applicazioni iOS native (come l'app Maps) usano un effetto simile. Per il backstory completo su come Apple riesce a realizzare questo nelle loro applicazioni e perché gli sviluppatori di SDK iOS devono ricorrere a un componente come Leaves, guarda "App Store-Safe Page Curl Animations" di Ole Begemann e "Apple's iBook Dynamic Page Curl" di Steven Troughton-Smith.
Torneremo su alcune delle teorie dietro il progetto Leaves, ma andiamo avanti e saltiamo nel creare il nostro Guerra dei mondi lettore per avere un'idea di come il codice appare in azione.
Il file di download allegato a questo post di Mobiletuts contiene il codice sorgente di Leaves e varie risorse di dominio pubblico (incluso il file Guerra dei mondi testo) utilizzato nel progetto.
Apri Xcode e crea un nuovo progetto usando il modello "View-based Application". Assegna un nome al progetto "WOTW" e seleziona "iPad" dal menu a discesa della famiglia di dispositivi.
Trascina e rilascia i seguenti file nel gruppo "File di supporto" in Xcode:
Tutti i file sopra riportati possono essere trovati nella cartella "Risorse" del download allegato a questo tutorial.
Successivamente, aggiungi il codice componente Leaves effettivo. Crea un nuovo gruppo chiamato "Foglie" nella cartella "WOTW" nel navigatore del progetto Xcode, quindi trascina e rilascia i seguenti file nel gruppo Foglie:
NOTA: assicurati di selezionare la casella di controllo "Copia elementi nella cartella del gruppo di destinazione" quando aggiungi i file sopra.
L'animazione creata da Leaves dipende dal framework QuartzCore. Di conseguenza, dovrai collegare questo framework al tuo progetto per far funzionare Leaves. Per fare ciò in Xcode 4, iniziare selezionando "WOTW" nel Navigatore progetto. Quindi, selezionare il target "WOTW" e quindi la scheda "Fasi di creazione". Quindi espandi il menu "Collega binarie con librerie" e fai clic sul simbolo più per aggiungere un nuovo framework. Infine, seleziona "QuartzCore" dall'elenco dei framework disponibili e fai clic su "Aggiungi". Il risultato di questo processo è visualizzato visivamente di seguito:
Per il nostro progetto, vogliamo che il lettore entri immediatamente nel Guerra dei mondi testo senza una pagina di lander. Per fare questo, dovremo fare il WOTWViewController
la classe eredita dal LeavesViewController
classe invece di direttamente da UIViewController
. Fallo aprendo WOTWViewController.h e modificando la dichiarazione dell'interfaccia come segue:
@interface WOTWViewController: LeavesViewController
La riga 1 sopra modifica WOTWViewController
da ereditare da LeavesViewController
invece di direttamente da UIViewController
. Perché LeavesViewController
da se stesso UIViewController
, puoi pensare al WOTWViewController
come il nipote di UIViewController
Quindi, cosa otteniamo per il nostro problema? LeavesViewController
è conforme all'origine dati Leaves e ai protocolli delegati (discussi in seguito) e definisce personalizzati -loadview
e -viewDidLoad
implementazioni che aggiungono un tipo speciale di UIView
chiamato a LeavesView
alla gerarchia della vista corrente. Il LeavesView
la classe è responsabile della maggior parte del lavoro dietro l'animazione del ricciolo della pagina.
In ordine per il WOTWViewController
classe per essere in grado di ereditare dal LeavesViewController
classe, abbiamo bisogno di importare il LeavesViewController
codice. Aggiungi i seguenti due #importare
linee a WOTWViewController.h:
#import "Utilities.h" #import "LeavesViewController.h"
Ti chiedi cosa Utilities.h
la dichiarazione di importazione è per? Come vedremo più avanti, il progetto Leaves si basa su una funzione intelligente dichiarata in quel file chiamato aspectFit
. Molti progetti Cocoa-Touch mantengono una classe Utilities per dichiarare e definire il codice helper utilizzato in tutta l'applicazione.
Per ora, dovremo dichiarare un solo membro di dati per il nostro progetto. Nel WOTWViewController.h file, aggiungi la seguente riga di codice:
CGPDFDocumentRef bookPDF;
Useremo "bookPDF" come riferimento al WOTW.pdf file più tardi. Il CGPDFDocumentRef
il tipo variabile verrà ampiamente utilizzato in questo tutorial e merita un'ulteriore esplorazione nella documentazione ufficiale di Apple.
Quando il WOTWViewController
viene creato abbiamo bisogno di inizializzare il bookPDF
membro dei dati dichiarato sopra. Come dimostrato nel progetto di esempio Leaves, puoi farlo con le seguenti linee di codice:
- (id) init if (self = [super init]) CFURLRef pdfURL = CFBundleCopyResourceURL (CFBundleGetMainBundle (), CFSTR ("WOTW.pdf"), NULL, NULL); bookPDF = CGPDFDocumentCreateWithURL ((CFURLRef) pdfURL); CFRelease (pdfURL); return self;
Vedi un problema con l'approccio sopra? Ricorda che stiamo caricando il nostro controller di visualizzazione da Interface Builder.
Il progetto di esempio Leaves presuppone che verrà creato manualmente a LeavesViewController
istanza programmaticamente chiamando il -(Id) init
metodo, così:
WOTWViewController * viewController = [[[WOTWViewController alloc] init];
Tuttavia, nel nostro progetto vogliamo annullare l'archiviazione del WOTWViewController
dal nostro Interface Builder NIB, quindi l'abitudine -(Id) init
la funzione appena implementata non verrà mai chiamata. Invece, dobbiamo fornire un'implementazione personalizzata per il -(Id) initWithCoder:
metodo per collegarsi al processo di annullamento dell'archiviazione NIB ed eseguire l'inizializzazione personalizzata. È bello lasciare il dentro
in posizione in modo da avere la possibilità di creare questo controller di visualizzazione manualmente, ma dovremmo creare un nuovo metodo per l'inizializzazione del PDF e chiamare quel metodo da entrambi -(Id) init
e -(Id) initWithCoder:
.
Aggiungi la seguente riga di codice al WOTWViewController.h file:
-(Vuoto) loadPDF;
Quindi, passare a WOTWViewController.m e implementare il metodo come segue:
-(void) loadPDF CFURLRef pdfURL = CFBundleCopyResourceURL (CFBundleGetMainBundle (), CFSTR ("WOTW.pdf"), NULL, NULL); bookPDF = CGPDFDocumentCreateWithURL ((CFURLRef) pdfURL); CFRelease (pdfURL);
Infine, chiama il loadPDF
metodo dagli inizializzatori di classe:
- (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;
Quando il nostro controller di visualizzazione personalizzato non è archiviato, il -(Id) initWithCoder:
il metodo verrà chiamato e chiamerà quindi loadPDF
metodo.
C'è solo un'altra cosa. Avendo allocata memoria per il bookPDF
riferimento, dovremmo anche rilasciare quella memoria quando avremo finito. Possiamo farlo aggiungendo la seguente riga di codice al -(Void) dealloc:
metodo:
CGPDFDocumentRelease (bookPDF);
Ricordiamo dal punto 5 che il LeavesViewController
la classe aggiunge automaticamente un'istanza di LeavesView
classe alla gerarchia della vista. Il LeavesView
la classe ha in qualche modo bisogno di scoprire quale contenuto dovrebbe essere visualizzato nella vista, e ciò si ottiene chiamando due metodi di origine dati personalizzati: -(NSUInteger) numberOfPagesInLeavesView:
e -(Void) renderPageAtIndex:
.
Per fornire un'implementazione personalizzata per questi, apri WOTWViewController.m e aggiungi le seguenti righe di codice:
#pragma mark LeavesViewDataSource methods - (NSUInteger) numberOfPagesInLeavesView: (LeavesView *) leavesView return CGPDFDocumentGetNumberOfPages (bookPDF); - (void) renderPageAtIndex: (NSUInteger) indice inContext: (CGContextRef) ctx CGPDFPageRef page = CGPDFDocumentGetPage (bookPDF, index + 1); CGAffineTransform transform = aspectFit (CGPDFPageGetBoxRect (pagina, kCGPDFMediaBox), CGContextGetClipBoundingBox (ctx)); CGContextConcatCTM (ctx, transform); CGContextDrawPDFPage (ctx, page);
Il -(NSUInteger) numberOfPagesInLeavesView:
il metodo indica semplicemente quante pagine del contenuto LeavesViewController
è responsabile della visualizzazione. Se stavi generando il contenuto per Leaves manualmente o se sapessi esattamente quante pagine erano nel PDF, puoi riportarlo manualmente qui. Certo, è sempre meglio determinare quel numero in modo dinamico, e questo è esattamente ciò che il CGPDFDocumentGetNumberOfPages ()
la funzione fa.
Il -(Void) renderPageAtIndex: InContext:
il metodo è responsabile per il disegno reale a CGContextRef
dopo aver aggiunto prima il contenuto PDF nel contesto passato come CTX
.
Costruisci ed esegui il progetto. Se tutto va bene, ora dovresti essere in grado di vedere il Guerra dei mondi copertina del libro ed essere in grado di sfogliare le pagine con l'animazione di arricciatura della pagina!
Il finale WOTWViewController.h il file dovrebbe assomigliare a questo:
#importare#importare #import "Utilities.h" #import "LeavesViewController.h" @interface WOTWViewController: LeavesViewController CGPDFDocumentRef bookPDF; - (void) loadPDF; @fine
E il finale WOTWViewController.m il file dovrebbe essere come segue:
#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) indice inContext: (CGContextRef) ctx CGPDFPageRef page = CGPDFDocumentGetPage (bookPDF, index + 1); CGAffineTransform transform = aspectFit (CGPDFPageGetBoxRect (pagina, kCGPDFMediaBox), CGContextGetClipBoundingBox (ctx)); CGContextConcatCTM (ctx, transform); CGContextDrawPDFPage (ctx, page); @fine
Nella mia ricerca su Leaves mi sono imbattuto in numerosi progetti open source aggiuntivi che aggiungono animazioni di transizione simili a pagine. Se hai trovato interessante questo progetto, dovresti anche controllare:
Lascia un commento qui sotto se desideri vedere un tutorial su uno dei progetti sopra!
Abbiamo fatto dei buoni progressi in questo tutorial. Ora possiamo interagire con il Guerra dei mondi PDF e l'animazione di arricciatura pagina aggiunge un tocco estetico all'eBook. Tuttavia, c'è ancora molto lavoro da fare prima che questa app sia pronta per il rilascio su App Store. Ad esempio, sarebbe fantastico se l'utente venisse automaticamente riportato all'ultima pagina a cui hanno accesso quando l'app si avvia e sarebbe anche molto bello avere un UISlider
o un componente di interfaccia simile per saltare rapidamente tra le sezioni del libro. Altre funzioni utili potrebbero includere ricerca, evidenziazione del testo, sommario o segnalibri.
Voglio assicurarmi che i miei tutorial SDK per iOS riguardino argomenti a cui è interessata la community Mobiletuts +. Pensi che dovrei creare un eReader completo e condividere il mio codice sorgente? O forse ti piacerebbe vedere un tutorial su qualcosa di completamente diverso? Rispondi al sondaggio seguente e fammi sapere!
AGGIORNAMENTO 9/7/2011: il sondaggio è ora chiuso. Oltre il 60% degli intervistati ha espresso interesse a vedere più post sull'aggiunta di un sommario e / o su un UISlider e un tutorial sull'uso di un UISlider è stato pubblicato (link sotto).
Puoi anche inviare il tuo feedback al mio account Twitter (@markhammonds), anche se ammetto che normalmente uso Twitter solo quando non lavoro su progetti freelance o sto scrivendo tutorial, quindi potresti semplicemente contattarmi via e-mail.