Questo tutorial ti insegnerà come utilizzare GPUImage per applicare i filtri delle immagini in tempo reale mentre viene visualizzato il feed della fotocamera del dispositivo. Lungo la strada, imparerai come popolare automaticamente le immagini all'interno di un controller di giostra e come ridimensionare le immagini con UIImage + Categorie.
Questo tutorial si basa su un post precedente intitolato "Costruisci un'applicazione fotografica con GPUImage". La lezione precedente ha dimostrato come usare UIImagePickerController
per selezionare le foto dall'album fotografico o dalla fotocamera del dispositivo, come aggiungere il GPUImage
libreria al tuo progetto e come usare il GPUImageFilter
classe per migliorare i fotogrammi della fotocamera. Se già conosci UIImagePickerController
e può capire come aggiungere GPUImage
al tuo progetto da solo, dovresti essere in grado di riprendere da dove l'ultimo tutorial si è interrotto.
Questo progetto farà ampio uso di un progetto open source chiamato iCarousel per aggiungere una visualizzazione elegante di foto selezionate.
Per includere iCarousel nel tuo progetto, vai alla pagina ufficiale di GitHub e scarica il codice sorgente come file zip. Estrarre il codice dal file ZIP e quindi trascinare la cartella dal titolo "iCarousel" in Xcode Project Navigator. Questa cartella dovrebbe contenere entrambi iCarousel.h e iCarousel.m. Assicurati di selezionare "Crea gruppi per eventuali cartelle aggiunte" e seleziona la casella accanto a "Copia gli elementi nella cartella del gruppo di destinazione (se necessario)" e la casella accanto al nome del target del progetto nell'area "Aggiungi ai target".
Avanti vai a ViewController.m e aggiungi una dichiarazione di importazione per iCarousel:
#import "ViewController.h" #import "GPUImage.h" #import "iCarousel / iCarousel.h"
Prima di visualizzare le nostre immagini con iCarousel, dovremo ridurle a una dimensione accettabile. Piuttosto che scrivere tutto il codice per farlo a mano, faremo uso dell'eccellente progetto UIImage + Categories, che fornisce funzionalità di base per il ridimensionamento delle immagini e alcuni altri trucchi di manipolazione delle immagini.
Mancia: In alternativa, puoi utilizzare il progetto MGImageUtilities per questa attività. Sebbene i dettagli di implementazione differiscano leggermente, fornisce anche un supporto eccellente per il ridimensionamento di UIImage.Scarica il UIImage + Categorie
codice da GitHub e quindi creare un nuovo gruppo con lo stesso nome in Xcode. Trascina entrambi i file di implementazione e di intestazione per UIImage + Alpha
, UIImage + Ridimensiona
, e UIImage + RoundedCorner
nel tuo progetto. Assicurati di selezionare "Crea gruppi per eventuali cartelle aggiunte" e seleziona la casella accanto a "Copia gli elementi nella cartella del gruppo di destinazione (se necessario)" e la casella accanto al nome del target del progetto nell'area "Aggiungi ai target".
All'interno del ViewController.m file, importa le categorie di immagini con la seguente riga di codice:
#import "ViewController.h" #import "GPUImage.h" #import "iCarousel.h" #import "UIImage + Resize.h"
Con il codice iCarousel importato nel nostro progetto, passa a MainStoryboard.storyboard file per rielaborare la nostra interfaccia.
Innanzitutto, seleziona la corrente UIImageView
collegato al selectedImageView
IBOutlet
ed eliminalo. Torna a ViewController.m e modificare il codice del progetto da leggere come segue:
@property (nonatomic, weak) IBOutlet iCarousel * photoCarousel; @property (nonatomic, weak) IBOutlet UIBarButtonItem * filterButton; @property (nonatomic, weak) IBOutlet UIBarButtonItem * saveButton; - (IBAction) photoFromAlbum; - (IBAction) fotoFromCamera; - (IBAction) saveImageToAlbum; - (IBAction) applyImageFilter: (id) mittente; @end @implementation ViewController @synthesize photoCarousel, filterButton, saveButton;
Nella riga 1 sopra, sostituire il selectedImageView
presa con a iCarousel
outlet chiamato photoCarousel
. Scambia anche le variabili nell'istruzione sintetizzare alla riga 14.
Torna a Interface Builder e trascina un nuovo UIView
sul controller della vista. Con il nuovo UIView
selezionato, vai alla scheda "Identity inspector" all'interno del pannello Utility e imposta il valore per il campo "Class" su "iCarousel". Questo dice a Interface Builder che il UIView
abbiamo aggiunto al progetto dovrebbe essere istanziato come un'istanza del iCarousel
classe.
Ora crea una connessione tra il photoCarousel
presa appena dichiarata e il UIView
appena aggiunto come sottoview.
Dobbiamo impostare sia l'origine dati sia il delegato photoCarousel
pure, e possiamo ottenere questo da Interface Builder. Per prima cosa vai a ViewController.h e dichiara che questo controller di visualizzazione si conformerà ai protocolli appropriati:
#importare#import "iCarousel / iCarousel.h" @interface ViewController: UIViewController
Alla riga 2 importiamo iCarousel e sulla linea 4 dichiariamo la conformità sia al delegato che all'origine dati.
Di nuovo nel file storyboard, ora puoi mappare sia l'origine dati che il delegato al controller della vista.
Prima di andare avanti, vai avanti e cambia il colore di sfondo del iCarousel
guarda al nero.
Ok, solo un'altra cosa. Vogliamo che la vista di iCarousel appaia sotto il UIToolbar
nella gerarchia della vista. Puoi farlo visivamente semplicemente trascinandoli nell'ordine corretto in Interface Builder:
Nota come la vista di iCarousel ora appare prima della barra degli strumenti.
Salva il tuo lavoro in Interface Builder.
iCarousel utilizza un modello di progettazione simile a UITableView
in quanto un'origine dati viene utilizzata per alimentare l'input nel controllo e un delegato viene utilizzato per gestire l'interazione con il controllo.
Per il nostro progetto, la fonte dei dati sarà semplice NSMutableArray
chiamato "displayImages". Aggiungi questo all'estensione di classe in ViewController.m adesso:
#import "UIImage + Resize.h" @interface ViewController () NSMutableArray * displayImages; @property (nonatomic, weak) IBOutlet iCarousel * photoCarousel;
Successivamente, vogliamo allocare memoria per l'array nell'inizializzatore designato della classe. Nel nostro caso, il controller della vista verrà istanziato da uno storyboard, quindi è l'inizializzatore corretto initWithCoder:
. Tuttavia, se la classe dovesse essere istanziata a livello di programmazione da un XIB, l'inizializzatore appropriato sarebbe initWithnibName: bundle:
. Per adattarsi allo stile di inizializzazione, scriveremo il nostro inizializzatore personalizzato e lo chiameremo da entrambi, in questo modo:
- (void) customSetup displayImages = [[NSMutableArray alloc] init]; - (id) initWithNibName: (NSString *) nibNameOrNil bundle: (NSBundle *) nibBundleOrNil if ((self = [super initWithNibName: nibNameOrNil bundle: nibBundleOrNil])) [self customSetup]; return self; - (id) initWithCoder: (NSCoder *) aDecoder if ((self = [super initWithCoder: aDecoder])) [self customSetup]; return self;
Ora possiamo implementare l'origine dati e delegare. Inizia con il metodo dell'origine dati numberOfItemsInCarousel:
, così:
#pragma mark #pragma mark iCarousel DataSource / Delegate / Custom - (NSUInteger) numberOfItemsInCarousel: (iCarousel *) carousel return [displayImages count];
Questo dirà a iCarousel quante immagini visualizzare visualizzando il numero di immagini memorizzate nell'array dell'origine dati.
Quindi, scrivi il metodo che genererà effettivamente una vista per ogni immagine visualizzata nel carosello:
- (UIView *) carousel: (iCarousel *) carousel viewForItemAtIndex: (NSUInteger) indice riutilizzareVisualizza: (vista UIView *) // Creare una nuova vista se nessuna vista è disponibile per il riciclaggio if (view == nil) view = [[UIImageView alloc] initWithFrame: CGRectMake (0, 0, 300.0f, 300.0f)]; view.contentMode = UIViewContentModeCenter; ((UIImageView *) view) .image = [displayImages objectAtIndex: index]; vista di ritorno;
Questo è un buon inizio, ma c'è un problema molto significativo con quanto sopra: le immagini dovrebbero essere ridimensionate prima di essere fornite a iCarousel. Aggiungere le seguenti righe di codice per aggiornare il metodo:
- (UIView *) carousel: (iCarousel *) carousel viewForItemAtIndex: (NSUInteger) indice riutilizzareVisualizza: (vista UIView *) // Creare una nuova vista se nessuna vista è disponibile per il riciclaggio if (view == nil) view = [[UIImageView alloc] initWithFrame: CGRectMake (0, 0, 300.0f, 300.0f)]; view.contentMode = UIViewContentModeCenter; // Ridimensiona in modo intelligente fino a un massimo di 250 px in larghezza o in altezza UIImage * originalImage = [displayImages objectAtIndex: index]; CGSize maxSize = CGSizeMake (250.0f, 250.0f); CGSize targetSize; // Se l'immagine è orizzontale, imposta larghezza su 250px e calcola in modo dinamico l'altezza se (originalImage.size.width> = originalImage.size.height) float newHeightMultiplier = maxSize.width / originalImage.size.width; targetSize = CGSizeMake (maxSize.width, round (originalImage.size.height * newHeightMultiplier)); // Se l'immagine è verticale, imposta l'altezza su 250px e calcola dinamicamente la larghezza else float newWidthMultiplier = maxSize.height / originalImage.size.height; targetSize = CGSizeMake (round (newWidthMultiplier * originalImage.size.width), maxSize.height); // Ridimensiona l'immagine di origine per adattarla bene alla vista di iCarousel ((UIImageView *)) .image = [[displayImages objectAtIndex: index] ridimensionatoImmagine: targetSize interpolationQuality: kCGInterpolationHigh]; vista di ritorno;Suggerimento Pro: Usando questo metodo in un'app di produzione? Prendi in considerazione il miglioramento del codice per le prestazioni eseguendo il ridimensionamento delle immagini su un thread in background e mantenendo un NSMutableArray separato che memorizza nella cache le versioni dell'immagine ridimensionate. AGGIORNAMENTO 27/09/2012: Nick Lockwood (autore di iCarousel) ha rilasciato un progetto chiamato FXImageView che gestirà automaticamente il caricamento delle immagini su un thread in background. Inoltre viene fornito con altri campanelli e fischietti utili come ombre e angoli arrotondati, quindi dai un'occhiata!
Sopra, abbiamo impostato una dimensione massima di 250 px per o la larghezza o l'altezza dell'immagine, quindi ridimensioniamo l'attributo opposto in modo che corrisponda. Questo vincola le proporzioni dell'immagine e sembra molto più bello del semplice ridimensionamento fino a 250px per 250px quadrati.
I due metodi di cui sopra sono tutti iCarousel deve iniziare a visualizzare le immagini.
Con i metodi delegato e origine dati configurati, ora è il momento giusto per configurare l'oggetto iCarousel in viewDidLoad
metodo pure:
- (void) viewDidLoad [super viewDidLoad]; // Configurazione di iCarousel self.photoCarousel.type = iCarouselTypeCoverFlow2; self.photoCarousel.bounces = NO;
Con poche altre modifiche, il progetto sarà in grado di visualizzare le immagini all'interno di un carosello!
In precedenza in questo tutorial, abbiamo sostituito il selectedImageView
proprietà con il photoCarousel
proprietà, ha aggiornato l'interfaccia Storyboard per farlo corrispondere e ha creato un NSMutableArray
per agire come il modello di dati di iCarousel. Tuttavia, ci sono alcuni metodi in ViewController.m utilizzando ancora il vecchio modello di dati che impedirà la compilazione del progetto, quindi risolviamo quelli ora. Aggiorna il saveImageToAlbum
metodo in questo modo:
- (IBAction) saveImageToAlbum UIImage * selectedImage = [displayImages objectAtIndex: self.photoCarousel.currentItemIndex]; UIImageWriteToSavedPhotosAlbum (selectedImage, self, @selector (image: didFinishSavingWithError: contextInfo :), nil);
La linea 3 seleziona il UIImage
dal modello dati che corrisponde all'indice iCarousel corrente. La riga 4 esegue l'effettiva scrittura del disco con quell'immagine.
Quindi, vai al UIImagePickerController
delegare il metodo e modificare il codice:
- (void) imagePickerController: (UIImagePickerController *) photoPicker didFinishPickingMediaWithInfo: (NSDictionary *) informazioni self.saveButton.enabled = YES; self.filterButton.enabled = YES; [displayImages addObject: [info valueForKey: UIImagePickerControllerOriginalImage]]; [self.photoCarousel reloadData]; [photoPicker dismissViewControllerAnimated: SÌ completamento: NULL];
Alla riga 6 sopra, aggiungiamo la foto selezionata al nuovo modello e sulla linea 8 forziamo un aggiornamento del carosello.
Solo un altro cambiamento da fare. Vai al foglio di azione clickedButtonAtIndex:
metodo e modificare il codice come segue:
#pragma mark - #pragma mark UIActionSheetDelegate - (void) actionSheet: (UIActionSheet *) actionSheet clickkedButtonAtIndex: (NSInteger) buttonIndex if (buttonIndex == actionSheet.cancelButtonIndex) return; GPUImageFilter * selectedFilter; switch (buttonIndex) caso 0: selectedFilter = [[GPUImageGrayscaleFilter alloc] init]; rompere; caso 1: selectedFilter = [[GPUImageSepiaFilter alloc] init]; rompere; caso 2: selectedFilter = [[GPUImageSketchFilter alloc] init]; rompere; case 3: selectedFilter = [[GPUImagePixellateFilter alloc] init]; rompere; case 4: selectedFilter = [[GPUImageColorInvertFilter alloc] init]; rompere; case 5: selectedFilter = [[GPUImageToonFilter alloc] init]; rompere; case 6: selectedFilter = [[GPUImagePinchDistortionFilter alloc] init]; rompere; caso 7: selectedFilter = [[GPUImageFilter alloc] init]; rompere; default: break; UIImage * filteredImage = [selectedFilter imageByFilteringImage: [displayImages objectAtIndex: self.photoCarousel.currentItemIndex]]; [displayImages replaceObjectAtIndex: self.photoCarousel.currentItemIndex withObject: filteredImage]; [self.photoCarousel reloadData];
Le ultime tre righe di questo metodo filtrano l'immagine del modello di dati che corrisponde all'indice del carosello corrente, sostituiscono il display del carosello con quell'immagine e quindi aggiornano il carosello.
Se tutto è andato bene, ora dovresti essere in grado di compilare ed eseguire il progetto! Ciò consentirà di visualizzare le immagini all'interno del carosello anziché semplicemente all'interno di una vista immagine.
L'app sembra buona finora, ma sarebbe bello se l'utente potesse rimuovere una foto dal carosello dopo averlo aggiunto al display. Nessun problema! Possiamo selezionare qualsiasi UIGestureRecognizer
sottoclasse per fare in modo che ciò accada. Per questo tutorial, ho scelto di utilizzare un doppio tocco con due dita. Questo gesto potrebbe non essere immediatamente intuitivo, ma è facile da eseguire e la complessità aggiunta aiuterà a prevenire la rimozione accidentale delle immagini.
All'interno del ViewController.m file, vai al carosello: viewForItemAtIndex: reusingView:
metodo e aggiungere le seguenti righe poco prima della fine del metodo:
// Ridimensiona l'immagine di origine per adattarla bene alla vista di iCarousel ((UIImageView *)) .image = [[displayImages objectAtIndex: index] ridimensionatoImmagine: targetSize interpolationQuality: kCGInterpolationHigh]; // Due dita tocca due volte per eliminare un'immagine UITapGestureRecognizer * gesture = [[UITapGestureRecognizer alloc] initWithTarget: self action: @selector (removeImageFromCarousel :)]; gesture.numberOfTouchesRequired = 2; gesture.numberOfTapsRequired = 2; view.gestureRecognizers = [NSArray arrayWithObject: gesture]; vista di ritorno;
Le righe 4 - 8 dichiarano una nuova UITapGestureRecognizer
oggetto, impostare il numero di tocchi (ovvero le dita) necessari per attivare il gesto su 2 e impostare anche il numero di tocchi richiesti per 2. Infine, appena prima di passare la vista all'oggetto iCarousel, impostiamo il file gestureRecognizers
proprietà con il riconoscitore appena formato.
Notare che quando viene attivato, questo indicatore di movimento attiva il selettore removeImageFromCarousel:
. Implementiamo quello successivo:
- (void) removeImageFromCarousel: (UIGestureRecognizer *) gesture [gesture removeTarget: self action: @selector (removeImageFromCarousel :)]; [displayImages removeObjectAtIndex: self.photoCarousel.currentItemIndex]; [self.photoCarousel reloadData];
La riga 3 rimuove il gesto dalla destinazione corrente per impedire l'attivazione di più gesti durante l'elaborazione. Le restanti due linee non sono nulla di nuovo a questo punto.
Costruisci ed esegui di nuovo l'app. Ora dovresti essere in grado di rimuovere dinamicamente gli elementi dal carosello!
Il resto di questo tutorial si concentrerà sull'utilizzo GPUImageStillCamera
per costruire un controllo personalizzato per il controllo di una fotocamera che possa applicare i filtri allo streaming video in entrata in tempo reale. GPUImageStillCamera
lavora a stretto contatto con una classe chiamata GPUImageView
. Fotogrammi fotocamera generati da GPUImageStillCamera
vengono inviati a un assegnato GPUImageView
oggetto da mostrare all'utente. Tutto ciò si ottiene con la funzionalità sottostante fornita da AVFoundation
framework, che fornisce accesso programmatico ai dati del frame della telecamera.
Perché GPUImageView
è un bambino di classe UIView
, possiamo incorporare l'intero display della fotocamera nella nostra abitudine UIViewController
classe.
Aggiungi un nuovo UIViewController
sottoclasse al progetto facendo clic con il tasto destro del mouse su "PhotoFX" nel navigatore del progetto e quindi selezionando Nuovo file> Classe Objective-C. Assegna un nome alla classe "MTCameraViewController" e inserisci "UIViewController" per il campo "sottoclasse di".
Fare clic su "Avanti" e quindi su "Crea" per completare il processo.
Vai al MTCameraViewController.m file e importazione GPUImage:
#import "MTCameraViewController.h" #import "GPUImage.h"
Quindi crea un'estensione di classe con i membri dati GPUImage necessari:
@interface MTCameraViewController ()GPUImageStillCamera * stillCamera; GPUImageFilter * filter; @fine
Infine, vai al viewDidLoad:
metodo e aggiungere il codice per avviare l'acquisizione della telecamera:
- (void) viewDidLoad [super viewDidLoad]; // Imposta filtro filtro telecamera iniziale = [[GPUImageFilter alloc] init]; [filtro prepareForImageCapture]; GPUImageView * filterView = (GPUImageView *) self.view; [filtro addTarget: filterView]; // Crea GPUImmagine telecamera fissa camera = [[GPUImageStillCamera alloc] init]; stillCamera.outputImageOrientation = UIInterfaceOrientationPortrait; [stillCamera addTarget: filter]; // Inizia a mostrare lo stream della videocamera [stillCamera startCameraCapture];
Le righe 5 - 9 creano un nuovo GPUImageView
per visualizzare l'alimentazione della telecamera e un valore predefinito GPUImageFilter
istanza per applicare un effetto speciale alla vista. Potremmo avere altrettanto facilmente usato uno dei GPUImageFilter
sottoclassi, come GPUImageSketchFilter
, ma partiremo invece con il filtro predefinito (cioè senza manipolazioni) e lasceremo che l'utente selezioni dinamicamente un filtro in un secondo momento.
Le righe 11 - 17 istanziano l'istanza della videocamera GPU e applicano il filtro creato in precedenza alla fotocamera prima di avviare l'acquisizione.
Prima che funzioni il codice del passaggio 8, è necessario aggiungere l'abitudine MTCameraViewController
classe appena creata per lo storyboard del progetto.
Apri il MainStoryboard.storyboard file e trascina un nuovo controller di visualizzazione dalla libreria degli oggetti. Con questo oggetto selezionato, vai alla scheda Identity inspector e imposta il valore del campo "Class" su "MTCameraViewController".
Quindi, trascina un UIToolbar
sullo schermo e imposta la sua proprietà di stile su "Black Opaque" nell'Inspector Attributes. Quindi aggiungi due elementi del pulsante della barra di larghezza flessibile alla barra degli strumenti con una "scatta foto" UIBarButtonItem
al centro.
Per connettere questo controller di visualizzazione al flusso dell'applicazione, fare clic con il pulsante destro del mouse sul pulsante "camera" dal controller della vista principale e trascinare l'uscita segues attivata nel nuovo controller di visualizzazione:
Quando richiesto, selezionare "Push" come stile di seguito.
Con l'oggetto seguito appena aggiunto ancora selezionato, vai su "Attributes inspector" e imposta l'identificatore su "pushMTCamera". Vai avanti e assicurati che "Push" sia selezionato dal menu a discesa "Stile".
Con il seguito creato, assicurarsi che il UIImagePicker
non verrà più visualizzato quando l'utente tocca il pulsante della fotocamera sulla prima schermata dell'app disconnettendo IBAction
uscita dal photoFromCamera
metodo.
Infine, seleziona la vista principale del nuovo MTCameraViewController. Vai a Identity inspector e imposta il valore della classe su "GPUImageView".
Anche se non è ancora perfetto, se ora costruisci ed esegui l'app, dovresti essere in grado di spingere MTCameraViewController
sulla gerarchia della vista e guarda GPUImageView
mostra i fotogrammi dalla fotocamera in tempo reale!
Ora possiamo aggiungere la logica necessaria per controllare il filtro applicato al display della fotocamera. Per prima cosa, vai al viewDidLoad:
metodo all'interno del MTCameraViewController.m file e aggiungi il codice che creerà un pulsante "Filtro" nella parte in alto a destra del controller della vista:
- (void) viewDidLoad [super viewDidLoad]; // Aggiungi il pulsante del filtro all'interfaccia UIBarButtonItem * filterButton = [[UIBarButtonItem alloc]] initWithTitle: @ Stile "Filter": UIBarButtonItemStylePlain target: self action: @selector (applyImageFilter :)]; self.navigationItem.rightBarButtonItem = filterButton;
Alla riga 6 sopra, creiamo un'abitudine UIBarButtonItem
che si innescherà applyImageFilter:
quando selezionato.
Ora crea il metodo di selezione:
- (IBAction) applyImageFilter: (id) sender UIActionSheet * filterActionSheet = [[UIActionSheet alloc] initWithTitle: @ "Seleziona filtro" delegato: self cancelButtonTitle: @ "Annulla" distructiveButtonTitle: nil otherButtonTitles: @ "Grayscale", @ "Sepia", @ "Schizzo", @ "Pixellate", @ "Inverti colore", @ "Toon", @ "Pinch Distort", @ "None", nil]; [filterActionSheet showFromBarButtonItem: sender animated: YES];
Dopo aver aggiunto quanto sopra, verrà visualizzato un avviso del compilatore che indica che il controller di visualizzazione corrente non è conforme a UIActionSheetDelegate
protocollo. Risolvi il problema ora andando a MTCameraViewController.h e modificando la dichiarazione di classe in questo modo:
#importare@interface MTCameraViewController: UIViewController @fine
Completa il cerchio tornando al MTCameraViewController.m file e aggiungendo la logica che risponderà al UIActionSheet
presentati:
- (void) actionSheet: (UIActionSheet *) actionSheet clickkedButtonAtIndex: (NSInteger) buttonIndex // Bail se il pulsante cancel è stato toccato if (actionSheet.cancelButtonIndex == buttonIndex) return; GPUImageFilter * selectedFilter; [stillCamera removeAllTargets]; [filtro removeAllTargets]; switch (buttonIndex) caso 0: selectedFilter = [[GPUImageGrayscaleFilter alloc] init]; rompere; caso 1: selectedFilter = [[GPUImageSepiaFilter alloc] init]; rompere; caso 2: selectedFilter = [[GPUImageSketchFilter alloc] init]; rompere; case 3: selectedFilter = [[GPUImagePixellateFilter alloc] init]; rompere; case 4: selectedFilter = [[GPUImageColorInvertFilter alloc] init]; rompere; case 5: selectedFilter = [[GPUImageToonFilter alloc] init]; rompere; case 6: selectedFilter = [[GPUImagePinchDistortionFilter alloc] init]; rompere; caso 7: selectedFilter = [[GPUImageFilter alloc] init]; rompere; default: break; filter = selectedFilter; GPUImageView * filterView = (GPUImageView *) self.view; [filtro addTarget: filterView]; [stillCamera addTarget: filter];
Le righe 11-12 vengono utilizzate per ripristinare il filtro attualmente selezionato.
Le righe 15 - 42 sopra dovrebbero sembrare familiari alla logica in ViewController.m; stiamo solo attivando il pulsante selezionato per creare un'istanza del filtro correlato.
Le righe 44 - 47 prendono il filtro appena creato e lo applicano alla videocamera GPUImage.
Se ora crei ed esegui il progetto, dovresti vedere che il pulsante del filtro appena creato consente all'utente di provare i filtri GPUImage in tempo reale!
Ora che i filtri del feed live funzionano, l'ultimo passaggio importante del tutorial è consentire all'utente di scattare istantanee con la videocamera GPUImage e quindi visualizzarle nuovamente nel carousel delle foto del controller della vista principale.
Per raggiungere questo obiettivo, passeremo i messaggi tra i controller della vista utilizzando il modello di progettazione della delega. In particolare, creeremo il nostro protocollo di delegato formale personalizzato in MTCameraViewController
e quindi configurare il main ViewController
classe per conformarsi a tale protocollo al fine di ricevere messaggi di delega.
Per iniziare, vai a MTViewController.h
e modificare il codice come segue:
#importare@protocol MTCameraViewControllerDelegate - (void) didSelectStillImage: (NSData *) image withError: (NSError *) error; @end @interface MTCameraViewController: delegato id UIViewController @property (debole, non anatomico); @fine
Il codice sopra dichiara un modello di delegato formale chiamato MTCameraViewControllerDelegate
alle righe 3-7 e quindi crea un oggetto delegato sulla riga 11.
Avanti passa a MTCameraViewController.m e sintetizzare la proprietà delegata:
@implementation MTCameraViewController @synthesize delegate;
Con il protocollo dichiarato, ora dobbiamo implementarlo nel main ViewController
classe. Vai a ViewController.h
e aggiungi le seguenti linee:
#importare#import "iCarousel.h" #import "MTCameraViewController.h" @interface ViewController: UIViewController @fine
Ora apri il ViewController.m file. Vogliamo assegnare la proprietà delegate quando il controller della vista viene istanziato. Poiché stiamo usando Storyboard, il posto giusto per farlo è nel prepareForSegue: mittente:
metodo, che verrà chiamato appena prima che il nuovo controller di visualizzazione venga spostato sullo schermo:
- (vuoto) prepareForSegue: (UIStoryboardSegue *) segue mittente: (id) sender if ([segue.identifier isEqualToString: @ "pushMTCamera"]) // Imposta il delegato in modo che questo controller possa ricevere foto scattate MTCameraViewController * cameraViewController = (MTCameraViewController *) follow.destinationViewController; cameraViewController.delegate = self;
Quindi dobbiamo implementare il didSelectStillImage: withError:
metodo richiesto dal MTCameraViewControllerDelegate
protocollo:
#pragma mark - #pragma mark MTCameraViewController // Questo metodo delegato viene chiamato dopo che la nostra classe di telecamere personalizzata scatta una foto - (void) didSelectStillImage: (NSData *) imageData withError: (NSError *) error if (! error) UIImage * image = [[UIImage alloc] initWithData: imageData]; [displayImages addObject: image]; [self.photoCarousel reloadData]; self.filterButton.enabled = YES; self.saveButton.enabled = YES; else UIAlertView * alert = [[UIAlertView alloc] initWithTitle: @ Messaggio "Capture Error": @ "Impossibile acquisire foto." delegate: nil cancelButtonTitle: @ "OK" otherButtonTitles: nil]; [avviso spettacolo];
Il codice sopra convertirà il NSData
oggetto consegnato al metodo a UIImage
e quindi ricaricare il carosello fotografico.
Infine, dobbiamo concludere le cose tornando a MTCameraViewController.m e aggiungendo nella chiamata al metodo delegato appropriato. Innanzitutto, imposta un IBAction
metodo che attiverà uno scatto della fotocamera:
GPUImageFilter * filter; - (IBAction) captureImage: (id) mittente; @fine
Prima di continuare, collegare questo metodo al pulsante "Scatta foto" nella MainStoryboard.storyboard file.
Infine, aggiungi l'implementazione del metodo:
-(IBAction) captureImage: (id) sender // Disabilita per impedire più tocchi durante l'elaborazione di UIButton * captureButton = (UIButton *) mittente; captureButton.enabled = NO; // Scatta l'immagine dalla fotocamera della GPU, rimanda al controller della vista principale [stillCamera capturePhotoAsJPEGProcessedUpToFilter: filter withCompletionHandler: ^ (NSData * processedJPEG, NSError * error) if ([delegate respondsToSelector: @selector (didSelectStillImage: withError :)]) [ self.delegate didSelectStillImage: processedJPEG withError: error]; else NSLog (@ "Il delegato non ha risposto al messaggio"); runOnMainQueueWithoutDeadlocking (^ [self.navigationController popToRootViewControllerAnimated: YES];); ];
Le righe 3-5 sopra disattivano il pulsante "Scatta foto" per evitare più pressioni durante l'elaborazione.
Le righe 7 - 22 usano il metodo GPUImage capturePhotoAsJPEGProcessedUpToF