Améliorer une application photo avec GPUImage & iCarousel

Ce tutoriel vous apprendra comment utiliser GPUImage pour appliquer des filtres d'image en temps réel lorsque le flux de caméra du périphérique est affiché. En cours de route, vous apprendrez à remplir automatiquement les images dans un contrôleur de carrousel et à redimensionner les images avec UIImage + Categories.


Aperçu du projet


Prérequis du tutoriel

Ce tutoriel s'appuie sur un précédent article intitulé "Construire une application photo avec GPUImage". La leçon précédente a montré comment utiliser UIImagePickerController pour sélectionner des photos à partir de l'album photo ou de l'appareil photo du périphérique, comment ajouter le GPUImage bibliothèque à votre projet, et comment utiliser le GPUImageFilter classe pour améliorer encore les cadres de la caméra. Si vous connaissez déjà UIImagePickerController et peut comprendre comment ajouter GPUImage à votre projet par vous-même, vous devriez être en mesure de reprendre où le dernier tutoriel s'est arrêté très bien.


Étape 1: Importer iCarousel

Ce projet fera largement appel à un projet open source appelé iCarousel afin d’afficher de manière élégante les photos sélectionnées..

Pour inclure iCarousel dans votre projet, rendez-vous sur la page officielle de GitHub et téléchargez le code source sous forme de fichier zip. Extrayez le code du fichier ZIP, puis faites glisser le dossier intitulé "iCarousel" dans le navigateur de projet Xcode. Ce dossier doit contenir à la fois iCarousel.h et iCarousel.m. Veillez à sélectionner "Créer des groupes pour les dossiers ajoutés" et à cocher la case "Copier les éléments dans le dossier du groupe de destination (si nécessaire)", ainsi que la case en regard du nom de la cible de votre projet dans la zone "Ajouter aux cibles"..

Ensuite, allez à ViewController.m et ajoutez une déclaration d'importation pour iCarousel:

 #import "ViewController.h" #import "GPUImage.h" #import "iCarousel / iCarousel.h"

Étape 2: Importer UIImage + Catégories

Avant d'afficher nos images avec iCarousel, nous devons les réduire à une taille acceptable. Plutôt que d'écrire tout le code pour le faire à la main, nous utiliserons l'excellent projet UIImage + Categories, qui fournit des fonctionnalités de base pour le redimensionnement des images, ainsi que quelques autres astuces de manipulation d'images..

Pointe: Vous pouvez également utiliser le projet MGImageUtilities pour cette tâche. Bien que les détails de la mise en œuvre diffèrent légèrement, il fournit également un excellent support pour la mise à l'échelle UIImage..

Téléchargez le UIImage + Catégories code de GitHub, puis créez un nouveau groupe du même nom dans Xcode. Faites glisser les fichiers d’implémentation et d’en-tête pour UIImage + Alpha, UIImage + Redimensionner, et UIImage + RoundedCorner dans votre projet. Veillez à sélectionner "Créer des groupes pour les dossiers ajoutés" et à cocher la case "Copier les éléments dans le dossier du groupe de destination (si nécessaire)", ainsi que la case en regard du nom de la cible de votre projet dans la zone "Ajouter aux cibles"..

Dans le ViewController.m fichier, importez les catégories d’image avec la ligne de code suivante:

 #import "ViewController.h" #import "GPUImage.h" #import "iCarousel.h" #import "UIImage + Resize.h"

Étape 3: Ajouter la vue iCarousel dans IB

Avec le code iCarousel importé dans notre projet, basculez vers le MainStoryboard.storyboard fichier à retravailler notre interface.

Tout d'abord, sélectionnez le courant UIImageView connecté au selectedImageView IBOutlet et supprimez-le. Revenez à ViewController.m et modifiez le code du projet pour lire comme suit:

 @property (nonatomic, faible) IBOutlet iCarousel * photoCarousel; @property (nonatomic, faible) IBOutlet UIBarButtonItem * filterButton; @property (nonatomic, faible) IBOutlet UIBarButtonItem * saveButton; - (IBAction) photoFromAlbum; - (IBAction) photoFromCamera; - (IBAction) saveImageToAlbum; - (IBAction) applyImageFilter: (id) expéditeur; @end @implementation ViewController @synthesize photoCarousel, filterButton, saveButton;

Sur la ligne 1 ci-dessus, remplacez le selectedImageView sortie avec un iCarousel sortie appelée photoCarousel. Échangez également les variables de la déclaration de synthèse à la ligne 14 ci-dessus.

Retournez à Interface Builder et faites glisser un nouveau UIView sur le contrôleur de vue. Avec le nouveau UIView sélectionnez l'onglet "Inspecteur d'identité" dans le volet Utilitaires et définissez la valeur du champ "Classe" sur "iCarousel". Ceci indique à Interface Builder que le UIView nous avons ajouté au projet devrait être instancié comme une instance de la iCarousel classe.

Maintenant, établissez une connexion entre le photoCarousel sortie vient de déclarer et la UIView vient d'être ajouté en tant que sous-vue.

Nous devons définir à la fois la source de données et le délégué pour photoCarousel Nous pouvons également réaliser cela à partir d’Interface Builder. D'abord, allez à ViewController.h et déclare que ce contrôleur de vue se conformera aux protocoles appropriés:

 #importation  #import "iCarousel / iCarousel.h" @interface ViewController: UIViewController 

Sur la ligne 2, nous importons iCarousel et sur la ligne 4, nous déclarons la conformité au délégué et à la source de données..

De retour dans le fichier de storyboard, vous pouvez maintenant mapper la source de données et le délégué au contrôleur de vue..

Avant de continuer, changez la couleur de fond de l'écran. iCarousel voir au noir.

Ok, juste une dernière chose. Nous voulons que la vue iCarousel apparaisse sous le UIToolbar dans la hiérarchie des vues. Vous pouvez le faire visuellement en les faisant simplement glisser dans le bon ordre dans Interface Builder:

Notez comment la vue iCarousel apparaît maintenant avant la barre d'outils..

Enregistrez votre travail dans Interface Builder.


Étape 4: Implémentation des protocoles iCarousel

iCarousel utilise un modèle similaire à UITableView en ce qu 'une source de données est utilisée pour alimenter le contrôle en entrée et un délégué pour gérer l'interaction avec le contrôle.

Pour notre projet, la source de données sera un simple NSMutableArray appelé "displayImages". Ajoutez ceci à l'extension de classe dans ViewController.m à présent:

 #import "UIImage + Resize.h" @interface ViewController () NSMutableArray * displayImages;  @property (nonatomic, faible) IBOutlet iCarousel * photoCarousel;

Ensuite, nous souhaitons allouer de la mémoire pour le tableau dans l'initialiseur désigné de la classe. Dans notre cas, le contrôleur de vue sera instancié à partir d’un Storyboard. L’initialiseur approprié est initWithCoder:. Cependant, si la classe devait être instanciée par programme à partir d’un XIB, l’initialiseur approprié serait initWithNibName: bundle:. Afin de s'adapter à l'un ou l'autre style d'initialisation, nous allons écrire notre propre initialiseur personnalisé et l'appeler à partir des deux, comme suit:

 - (void) customSetup displayImages = [[NSMutableArray alloc] init];  - (id) initWithNibName: (NSString *) bundle nibNameOrNil: (NSBundle *) nibBundleOrNil if ((self = [super initWithNibName: nibNameOrNil bundle: nibBundleOrNil])) [self selfSetup];  retourner soi-même;  - (id) initWithCoder: (NSCoder *) aDecoder if ((self = [super initWithCoder: aDecoder]))) [self customSetup];  retourner soi-même; 

Maintenant, nous pouvons implémenter la source de données et déléguer. Commencez avec la méthode de la source de données numberOfItemsInCarousel:, ainsi:

 #pragma mark #pragma mark iCarousel DataSource / Délégué / Personnalisé - (NSUInteger) numberOfItemsInCarousel: (iCarousel *) carrousel return [displayImages count]; 

Cela indiquera à iCarousel le nombre d'images à afficher en examinant le nombre d'images stockées dans le tableau de la source de données..

Ensuite, écrivez la méthode qui générera une vue pour chaque image affichée dans le carrousel:

 - Carrousel (UIView *): Carrousel (iCarousel *) viewForItemAtIndex: (NSUInteger) index reusingView: (UIView *) view // Crée une nouvelle vue si aucune vue n'est disponible pour le recyclage si (view == nil) view = [UIImageView alloc] initWithFrame: CGRectMake (0, 0, 300.0f, 300.0f)]; view.contentMode = UIViewContentModeCenter;  (Vue (UIImageView *)) .image = [displayImages objectAtIndex: index]; retourner la vue; 

C'est un bon début, mais il y a un problème très important avec ce qui précède: les images doivent être réduites avant d'être fournies à iCarousel. Ajoutez les lignes de code suivantes pour mettre à jour la méthode:

 - Carrousel (UIView *): Carrousel (iCarousel *) viewForItemAtIndex: (NSUInteger) index reusingView: (UIView *) view // Crée une nouvelle vue si aucune vue n'est disponible pour le recyclage si (view == nil) view = [UIImageView alloc] initWithFrame: CGRectMake (0, 0, 300.0f, 300.0f)]; view.contentMode = UIViewContentModeCenter;  // Réduisez intelligemment la largeur ou la hauteur jusqu'à un maximum de 250 pixels UIImage * originalImage = [displayImages objectAtIndex: index]; CGSize maxSize = CGSizeMake (250.0f, 250.0f); CGSize targetSize; // Si image est paysage, définissez width sur 250px et déterminez de façon dynamique la hauteur if (originalImage.size.width> = originalImage.size.height) float newHeightMultiplier = maxSize.width / originalImage.size.width; targetSize = CGSizeMake (maxSize.width, round (originalImage.size.height * newHeightMultiplier));  // Si image est portrait, définissez height sur 250px et déterminez la largeur de manière dynamique else float newWidthMultiplier = maxSize.height / originalImage.size.height; targetSize = CGSizeMake (round (newWidthMultiplier * originalImage.size.width), maxSize.height);  // Redimensionne l'image source pour qu'elle s'adapte parfaitement à iCarousel (vue (UIImageView *)) .image = [[displayImages objectAtIndex: index] resizedImage: targetSize interpolationQuality: kCGInterpolationHigh]; retourner la vue; 
Conseil pro: Vous utilisez cette méthode dans une application de production? Envisagez d'améliorer le code pour améliorer les performances en effectuant le redimensionnement de l'image sur un thread en arrière-plan et en conservant un NSMutableArray distinct qui met en cache les versions d'image réduites. MISE À JOUR 27/09/2012: Nick Lockwood (auteur de iCarousel) a publié un projet appelé FXImageView qui gérera automatiquement le chargement des images sur un fil d’arrière-plan. Il est également livré avec d'autres options utiles telles que des ombres portées et des coins arrondis, alors jetez-y un œil!

Ci-dessus, nous fixons une taille maximale de 250 px pour non plus la largeur ou la hauteur de l'image, puis nous réduisons l'attribut opposé pour le faire correspondre. Cela limite les proportions de l'image et est beaucoup plus esthétique qu'une simple réduction à un carré de 250 pixels par 250 pixels..

Les deux méthodes ci-dessus sont toutes nécessaires à iCarousel pour commencer à afficher des images..

Avec les méthodes de délégué et de source de données configurées, le moment est venu de configurer l’objet iCarousel dans le viewDidLoad méthode aussi bien:

 - (void) viewDidLoad [super viewDidLoad]; // Configuration iCarousel self.photoCarousel.type = iCarouselTypeCoverFlow2; self.photoCarousel.bounces = NO; 

Avec quelques ajustements supplémentaires, le projet pourra afficher des images dans un carrousel.!


Étape 5: passer au nouveau modèle de données

Plus tôt dans ce tutoriel, nous avons remplacé le selectedImageView propriété avec le photoCarousel propriété, mis à jour l'interface Storyboard pour correspondre, et créé un NSMutableArray d'agir comme modèle de données iCarousel. Cependant, il existe quelques méthodes dans ViewController.m toujours en utilisant l'ancien modèle de données qui empêchera le projet de compiler, corrigeons-les maintenant. Mettre à jour le saveImageToAlbum méthode comme suit:

 - (IBAction) saveImageToAlbum UIImage * selectedImage = [displayImages objectAtIndex: self.photoCarousel.currentItemIndex]; UIImageWriteToSavedPhotosAlbum (selectedImage, self, @selector (image: didFinishSavingWithError: contextInfo :), nil); 

La ligne 3 sélectionne le UIImage à partir du modèle de données qui correspond à l'index iCarousel actuel. La ligne 4 effectue l’écriture réelle du disque avec cette image.

Ensuite, allez au UIImagePickerController méthode delegate et modifiez le code:

 - (void) imagePickerController: (UIImagePickerController *) photoPicker didFinishPickingMediaWithInfo: (NSDictionary *) info self.saveButton.enabled = YES; self.filterButton.enabled = YES; [displayImages addObject: [info valueForKey: UIImagePickerControllerOriginalImage]]; [self.photoCarousel reloadData]; [photoPicker licencieViewControllerAnimated: YES complétion: NULL]; 

Sur la ligne 6 ci-dessus, nous ajoutons la photo sélectionnée au nouveau modèle et sur la ligne 8, nous forçons un rafraîchissement du carrousel..

Encore un changement à faire. Aller à la fiche action clickedButtonAtIndex: et modifiez le code comme suit:

 #pragma mark - #pragma mark UIActionSheetDelegate - (void) actionSheet: (UIActionSheet *) actionSheet clickedButtonAtIndex: (NSInteger) buttonIndex if (buttonIndex == actionSheet.cancelButtonIndex) return;  GPUImageFilter * selectedFilter; commutateur (buttonIndex) cas 0: selectedFilter = [[GPUImageGrayscaleFilter alloc] init]; Pause; cas 1: selectedFilter = [[GPUImageSepiaFilter alloc] init]; Pause; cas 2: selectedFilter = [[GPUImageSketchFilter alloc] init]; Pause; cas 3: selectedFilter = [[GPUImagePixellateFilter alloc] init]; Pause; cas 4: selectedFilter = [[GPUImageColorInvertFilter alloc] init]; Pause; cas 5: selectedFilter = [[GPUImageToonFilter alloc] init]; Pause; cas 6: selectedFilter = [[GPUImagePinchDistortionFilter alloc] init]; Pause; cas 7: selectedFilter = [[GPUImageFilter alloc] init]; Pause; défaut: break;  UIImage * filterImage = [selectedFilter imageByFilteringImage: [displayImages objectAtIndex: self.photoCarousel.currentItemIndex]]; [displayImages replaceObjectAtIndex: self.photoCarousel.currentItemIndex withObject: filterImage]; [self.photoCarousel reloadData]; 

Les trois dernières lignes de cette méthode filtreront l'image du modèle de données qui correspond à l'index actuel du carrousel, remplaceront l'affichage du carrousel par cette image, puis actualiseront le carrousel..

Si tout se passe bien, vous devriez maintenant pouvoir compiler et exécuter le projet! Cela vous permettra de visualiser vos images dans le carrousel plutôt que simplement dans une vue..


Étape 6: Ajouter un geste pour supprimer des images

Jusqu'à présent, l'application semble bien, mais il serait bien que l'utilisateur puisse supprimer une photo du carrousel après l'avoir ajoutée à l'écran. Aucun problème! Nous pouvons sélectionner UIGestureRecognizer sous-classe pour y arriver. Pour ce tutoriel, j'ai choisi d'utiliser un double-tap à deux doigts. Ce geste n'est peut-être pas immédiatement intuitif, mais il est facile à exécuter et la complexité accrue aidera à éviter la suppression accidentelle d'images..

Dans le ViewController.m fichier, allez au carrousel: viewForItemAtIndex: reusingView: méthode et ajoutez les lignes suivantes juste avant la fin de la méthode:

 // Redimensionne l'image source pour qu'elle s'adapte parfaitement au format iCarousel ((vue UIImageView *)) .image = [[displayImages objectAtIndex: index] resizedImage: targetSize interpolationQuality: kCGInterpolationHigh]; // Une double pression sur deux doigts supprime une image UITapGestureRecognizer * gesture = [[UITapGestureRecognizer alloc] initWithTarget: auto action: @selector (removeImageFromCarousel :)]; gesture.numberOfTouchesRequired = 2; gesture.numberOfTapsRequired = 2; view.gestureRecognizers = [NSArray arrayWithObject: gesture]; retourner la vue;

Lignes 4 - 8 déclarer un nouveau UITapGestureRecognizer objet, définissez le nombre de touches (c'est-à-dire les doigts) nécessaires pour déclencher le geste sur 2 et définissez le nombre de taps requis sur 2 également. Enfin, juste avant de revenir à l’objet iCarousel, nous définissons le paramètre gesteReconnaissances propriété avec le dispositif de reconnaissance nouvellement formé.

Notez que lorsqu’il est déclenché, cette reconnaissance de geste déclenche le sélecteur removeImageFromCarousel:. Implémentons cette prochaine:

 - (void) removeImageFromCarousel: (UIGestureRecognizer *) geste [geste effaceTarget: autoaction: @selector (removeImageFromCarousel :)]; [displayImages removeObjectAtIndex: self.photoCarousel.currentItemIndex]; [self.photoCarousel reloadData]; 

La ligne 3 supprime le geste de la cible actuelle pour éviter que plusieurs gestes ne soient déclenchés pendant le traitement. Les deux lignes restantes ne sont pas nouvelles à ce stade.

Générez et exécutez l'application à nouveau. Vous devriez maintenant pouvoir supprimer dynamiquement des objets du carrousel.!


Étape 7: Créer MTCameraViewController

Le reste de ce tutoriel se concentrera sur l’utilisation de GPUImageStillCamera créer un contrôle de sélecteur de caméra personnalisé pouvant appliquer des filtres au flux vidéo entrant en temps réel. GPUImageStillCamera travaille en étroite collaboration avec une classe appelée GPUImageView. Cadres de caméra générés par GPUImageStillCamera sont envoyés à un assigné GPUImageView objet à afficher à l'utilisateur. Tout cela est accompli avec la fonctionnalité sous-jacente fournie par le AVFoundation cadre, qui fournit un accès par programme aux données de trame de la caméra.

Parce que GPUImageView est une classe enfant de UIView, nous pouvons intégrer la totalité de l'écran de la caméra dans notre propre coutume UIViewController classe.

Ajouter un nouveau UIViewController sous-classe du projet en cliquant avec le bouton droit de la souris sur "PhotoFX" dans le navigateur de projet, puis en sélectionnant Nouveau fichier> classe Objective-C. Nommez la classe "MTCameraViewController" et entrez "UIViewController" dans le champ "sous-classe de".

Cliquez sur "Suivant" puis "Créer" pour terminer le processus..

Aller au MTCameraViewController.m déposer et importer GPUImage:

 #import "MTCameraViewController.h" #import "GPUImage.h"

Créez ensuite une extension de classe avec les membres de données GPUImage nécessaires:

 @interface MTCameraViewController ()  GPUImageStillCamera * stillCamera; Filtre GPUImageFilter *;  @fin

Enfin, allez au viewDidLoad: Méthode et ajoutez le code pour démarrer la capture de la caméra:

 - (void) viewDidLoad [super viewDidLoad]; // Configuration du filtre de caméra initial filter = [[GPUImageFilter alloc]] init]; [filtre prepareForImageCapture]; GPUImageView * filterView = (GPUImageView *) self.view; [filter addTarget: filterView]; // Créer une caméra GPUImage personnalisée stillCamera = [[GPUImageStillCamera alloc] init]; stillCamera.outputImageOrientation = UIInterfaceOrientationPortrait; [stillCamera addTarget: filter]; // Commencer à montrer le flux de la caméra vidéo [stillCamera startCameraCapture]; 

Les lignes 5 à 9 créent un nouveau GPUImageView pour afficher le flux de la caméra et un défaut GPUImageFilter exemple pour appliquer un effet spécial à la vue. Nous aurions pu tout aussi facilement utiliser l'un des GPUImageFilter sous-classes, telles que GPUImageSketchFilter, mais nous allons plutôt commencer avec le filtre par défaut (c'est-à-dire aucune manipulation) et laisser l'utilisateur sélectionner dynamiquement un filtre plus tard.

Les lignes 11 à 17 instancient l’instance de caméra GPU et appliquent le filtre créé précédemment à la caméra avant de commencer la capture..


Étape 8: Ajouter la caméra personnalisée dans IB

Avant que le code de l’étape 8 ne fonctionne, nous devons ajouter la commande personnalisée. MTCameraViewController classe vient de créer pour le Storyboard du projet.

Ouvrez le MainStoryboard.storyboard fichier et faites glisser un nouveau contrôleur de vue à partir de la bibliothèque d'objets. Avec cet objet sélectionné, accédez à l'onglet Inspecteur d'identité et définissez la valeur du champ "Classe" sur "MTCameraViewController"..

Ensuite, faites glisser un UIToolbar sur l'écran et définissez sa propriété de style sur "Black Opaque" dans l'inspecteur Attributs. Ajoutez ensuite deux éléments de bouton de barre de largeur flexibles à la barre d’outils avec un "Prendre une photo". UIBarButtonItem dans le centre.

Pour connecter ce contrôleur de vue au flux d’application, cliquez avec le bouton droit de la souris sur le bouton "caméra" du contrôleur de vue principal et faites glisser la sortie des séquences déclenchée vers le nouveau contrôleur de vue:

Lorsque vous y êtes invité, sélectionnez "Push" comme style de classement..

Avec l'objet Segue nouvellement ajouté toujours sélectionné, accédez à "Inspecteur d'attributs" et définissez l'identificateur sur "pushMTCamera". Allez-y et assurez-vous que "Push" est sélectionné dans le menu déroulant "Style".

Avec la segue créée, assurez-vous que le UIImagePicker ne sera plus affiché lorsque l'utilisateur tapera sur le bouton de l'appareil photo sur le premier écran de l'application en déconnectant le IBAction sortie de la photoFromCamera méthode.

Enfin, sélectionnez la vue principale du MTCameraViewController nouvellement créé. Accédez à l'inspecteur d'identité et définissez la valeur de la classe sur "GPUImageView"..

Bien que pas encore parfait, si vous construisez et exécutez l’application maintenant, vous devriez pouvoir MTCameraViewController sur la hiérarchie de vue et regarder GPUImageView afficher les images de la caméra en temps réel!


Étape 9: Ajouter une sélection de filtre en temps réel

Nous pouvons maintenant ajouter la logique nécessaire pour contrôler le filtre appliqué à l’affichage de la caméra. D'abord, allez au viewDidLoad: méthode dans le MTCameraViewController.m fichier et ajoutez le code qui créera un bouton "Filtre" en haut à droite du contrôleur de vue:

 - (void) viewDidLoad [super viewDidLoad]; // Ajouter un bouton de filtre à l'interface UIBarButtonItem * filterButton = [[UIBarButtonItem alloc]] initWithTitle: @ style "Filter": UIBarButtonItemStylePlain cible: action autonome: @selector (applyImageFilter :)]; self.navigationItem.rightBarButtonItem = filterButton;

A la ligne 6 ci-dessus, nous créons un UIBarButtonItem cela déclenchera applyImageFilter: lorsque sélectionné.

Maintenant, créez la méthode de sélection:

 - (IBAction) applyImageFilter: (id) expéditeur UIActionSheet * filterActionSheet = [[UIActionSheet alloc]] initWithTitle: @ "Sélectionner un filtre" délégué: auto cancelButtonTitle: @ "Cancel" destructiveButtonTitle: nil otherButtonTitles: @ "Grayscale" @ "Esquisse", @ "Pixellate", @ "Inverser la couleur", @ "Toon", @ "Distorsion du pincement", @ "Aucune", nil]; [filterActionSheet showFromBarButtonItem: expéditeur animé: OUI]; 

Après avoir ajouté ce qui précède, vous verrez un avertissement du compilateur indiquant que le contrôleur de vue actuel ne se conforme pas à la UIActionSheetDelegate protocole. Résoudre ce problème maintenant en allant à MTCameraViewController.h et en modifiant la déclaration de classe comme suit:

 #importation  @interface MTCameraViewController: UIViewController  @fin

Complétez le cercle en revenant à la MTCameraViewController.m fichier et en ajoutant la logique qui répondra à la UIActionSheet présenté:

 - (void) actionSheet: (UIActionSheet *) actionSheet clickedButtonAtIndex: (NSInteger) buttonIndex // Bail si le bouton d'annulation a été activé si (actionSheet.cancelButtonIndex == buttonIndex) return;  GPUImageFilter * selectedFilter; [stillCamera removeAllTargets]; [filtre removeAllTargets]; commutateur (buttonIndex) cas 0: selectedFilter = [[GPUImageGrayscaleFilter alloc] init]; Pause; cas 1: selectedFilter = [[GPUImageSepiaFilter alloc] init]; Pause; cas 2: selectedFilter = [[GPUImageSketchFilter alloc] init]; Pause; cas 3: selectedFilter = [[GPUImagePixellateFilter alloc] init]; Pause; cas 4: selectedFilter = [[GPUImageColorInvertFilter alloc] init]; Pause; cas 5: selectedFilter = [[GPUImageToonFilter alloc] init]; Pause; cas 6: selectedFilter = [[GPUImagePinchDistortionFilter alloc] init]; Pause; cas 7: selectedFilter = [[GPUImageFilter alloc] init]; Pause; défaut: break;  filter = selectedFilter; GPUImageView * filterView = (GPUImageView *) self.view; [filter addTarget: filterView]; [stillCamera addTarget: filter]; 

Les lignes 11 à 12 servent à réinitialiser le filtre actuellement sélectionné..

Les lignes 15 à 42 ci-dessus devraient sembler familières à la logique ViewController.m; nous ne faisons que passer sur le bouton sélectionné pour créer une instance du filtre de corrélation.

Les lignes 44 à 47 prennent le filtre nouvellement créé et l'appliquent à la caméra GPUImage.

Si vous générez et exécutez le projet maintenant, vous devriez voir que le bouton de filtre nouvellement créé permet à l'utilisateur d'essayer des filtres GPUImage en temps réel.!


Étape 10: Créer un protocole de délégué de caméra

Maintenant que les filtres de flux en direct fonctionnent, la dernière étape majeure du didacticiel consiste à permettre à l'utilisateur de prendre des instantanés avec la caméra GPUImage, puis de les afficher à nouveau dans le carrousel de photos du contrôleur de la vue principale..

Pour ce faire, nous allons passer des messages entre les contrôleurs de vue à l'aide du modèle de conception de délégation. Plus précisément, nous allons créer notre propre protocole de délégation formel personnalisé dans MTCameraViewController puis configurez le principal ViewController classe à se conformer à ce protocole afin de recevoir des messages de délégation.

Pour commencer, allez à MTViewController.h et modifiez le code comme suit:

 #importation  @protocol MTCameraViewControllerDelegate - (void) didSelectStillImage: (NSData *) image withError: (NSError *) error; @end @interface MTCameraViewController: UIViewController @property (faible, non atomique) id delegate; @fin

Le code ci-dessus déclare un modèle de délégué formel appelé MTCameraViewControllerDelegate sur les lignes 3 à 7, puis crée un objet délégué sur la ligne 11.

Passez ensuite à MTCameraViewController.m et synthétisez la propriété delegate:

 @implementation MTCameraViewController @synthesize delegate;

Avec le protocole déclaré, nous devons maintenant le mettre en œuvre dans le ViewController classe. Aller à ViewController.h et ajoutez les lignes suivantes:

 #importation  #import "iCarousel.h" #import "MTCameraViewController.h" @interface ViewController: UIViewController  @fin

Maintenant, ouvrez le ViewController.m fichier. Nous voulons affecter la propriété delegate lorsque le contrôleur de vue est instancié. Parce que nous utilisons Storyboards, le bon endroit pour faire ceci est dans le prepareForSegue: expéditeur: méthode, qui sera appelée juste avant que le nouveau contrôleur de vue ne soit affiché à l'écran:

 - prepare (void) prepareForSegue: (UIStoryboardSegue *) seender sender: (id) sender if ([segue.identifier isEqualToString: @ "pushMTCamera")) // Définissez le délégué pour que ce contrôleur puisse recevoir des photos capturées MTCameraViewController * *) segue.destinationViewController; cameraViewController.delegate = self; 

Ensuite, nous devons mettre en œuvre le didSelectStillImage: withError: méthode requise par le MTCameraViewControllerDelegate protocole:

 #pragma mark - #pragma mark MTCameraViewController // Cette méthode de délégation est appelée après que notre classe d'appareil photo personnalisée a pris une photo - (void) didSelectStillImage: (NSData *) imageData withError: (NSError *) error if (! error) UUmage * 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: @ message "Erreur de capture": @ "Impossible de capturer une photo." délégué: nil cancelButtonTitle: @ "OK" otherButtonTitles: nil]; [émission d'alerte]; 

Le code ci-dessus convertira le NSData objet remis à la méthode à un UIImage puis rechargez le carrousel de photos.

Enfin, nous devons terminer les choses en retournant à MTCameraViewController.m et en ajoutant l'appel de la méthode déléguée appropriée. Tout d'abord, configurez un IBAction méthode qui déclenchera un claquement de caméra:

 Filtre GPUImageFilter *;  - (IBAction) captureImage: (id) expéditeur; @fin

Avant de continuer, connectez cette méthode au bouton "Prendre une photo" dans le MainStoryboard.storyboard fichier.

Enfin, ajoutez l'implémentation de la méthode:

 -(IBAction) captureImage: (id) sender // Désactiver pour empêcher plusieurs taps lors du traitement de UIButton * captureButton = (UIButton *) expéditeur; captureButton.enabled = NO; // Image instantanée de la caméra GPU, à renvoyer au contrôleur de la vue principale [stillCamera capturePhotoAsJPEGProcessedUpToFilter: filter withCompletionHandler: ^ (NSData * processingJPEG, NSError * error) if self.delegate didSelectStillImage: processingJPEG withError: error];  else NSLog (@ "Le délégué n'a pas répondu au message");  runOnMainQueueWithoutDeadlocking (^ [self.navigationController popToRootViewControllerAnimated: YES];); ]; 

Les lignes 3 à 5 ci-dessus désactivent le bouton "Prendre une photo" pour éviter de multiples pressions pendan