Foto-App mit GPUImage & iCarousel verbessern

In diesem Lernprogramm erfahren Sie, wie Sie mit GPUImage Bildfilter in Echtzeit anwenden, während der Kamera-Feed des Geräts angezeigt wird. Auf dem Weg erfahren Sie, wie Sie Bilder innerhalb eines Karussellcontrollers automatisch auffüllen und die Größe von Bildern mit UIImage + Categories ändern.


Projektübersicht


Lernprogramm-Voraussetzungen

Dieses Tutorial baut auf einem früheren Beitrag mit dem Titel "Erstellen einer Foto-App mit GPUImage" auf. In der vorherigen Lektion wurde die Verwendung demonstriert UIImagePickerController So wählen Sie Fotos aus dem Fotoalbum oder der Kamera des Geräts aus GPUImage Bibliothek zu Ihrem Projekt und zur Verwendung der GPUImageFilter Klasse, um die Standbilder der Kamera zu verbessern. Wenn Sie bereits vertraut sind UIImagePickerController und kann herausfinden, wie man hinzufügt GPUImage Um Ihr Projekt auf eigene Faust zu erreichen, sollten Sie in der Lage sein, an der Stelle zu beginnen, an der das letzte Tutorial gerade aufgehört hat.


Schritt 1: Importieren Sie iCarousel

Bei diesem Projekt wird ein Open-Source-Projekt namens iCarousel umfassend genutzt, um ausgewählte Fotos stilvoll darzustellen.

Um iCarousel in Ihr Projekt aufzunehmen, besuchen Sie die offizielle GitHub-Seite und laden Sie den Quellcode als ZIP-Datei herunter. Extrahieren Sie den Code aus der ZIP-Datei und ziehen Sie den Ordner mit dem Titel "iCarousel" in den Xcode Project Navigator. Dieser Ordner sollte beides enthalten iCarousel.h und iCarousel.m. Wählen Sie "Gruppen für hinzugefügte Ordner erstellen" aus und aktivieren Sie das Kontrollkästchen neben "Elemente in den Ordner der Zielgruppe kopieren (falls erforderlich)" sowie das Kontrollkästchen neben dem Zielnamen Ihres Projekts im Bereich "Zu Zielen hinzufügen".

Weiter gehts zu ViewController.m und fügen Sie eine Importanweisung für iCarousel hinzu:

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

Schritt 2: Importieren Sie UIImage + Categories

Bevor wir unsere Bilder mit iCarousel anzeigen, müssen wir sie auf eine akzeptable Größe verkleinern. Anstatt den gesamten Code dafür von Hand zu schreiben, verwenden wir das hervorragende UIImage + Categories-Projekt, das grundlegende Funktionen für die Größenänderung von Bildern sowie einige andere Bildmanipulations-Tricks bietet.

Spitze: Sie können alternativ das Projekt MGImageUtilities für diese Aufgabe verwenden. Die Implementierungsdetails unterscheiden sich zwar geringfügig, bieten jedoch auch eine hervorragende Unterstützung für die UIImage-Skalierung.

Laden Sie das herunter UIImage + Kategorien Code von GitHub und erstellen Sie eine neue Gruppe mit demselben Namen in Xcode. Ziehen Sie sowohl die Implementierungs- als auch die Header-Datei für UIImage + Alpha, UIImage + Größe ändern, und UIImage + RoundedCorner in dein Projekt. Wählen Sie "Gruppen für hinzugefügte Ordner erstellen" aus und aktivieren Sie das Kontrollkästchen neben "Elemente in den Ordner der Zielgruppe kopieren (falls erforderlich)" sowie das Kontrollkästchen neben dem Zielnamen Ihres Projekts im Bereich "Zu Zielen hinzufügen".

Innerhalb des ViewController.m Datei importieren Sie die Bildkategorien mit der folgenden Codezeile:

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

Schritt 3: Fügen Sie die iCarousel-Ansicht in IB hinzu

Wenn der iCarousel-Code in unser Projekt importiert wurde, wechseln Sie zur MainStoryboard.storyboard Datei, um unsere Schnittstelle zu überarbeiten.

Wählen Sie zuerst den Strom aus UIImageView verbunden mit dem selectedImageView IBOutlet und lösche es. Wechseln Sie wieder zu ViewController.m und ändern Sie den Projektcode wie folgt:

 @ Eigenschaft (nichtatomisch, schwach) IBOutlet iCarousel * photoCarousel; @ property (nichtatomisch, schwach) IBOutlet UIBarButtonItem * filterButton; @ property (nichtatomisch, schwach) IBOutlet UIBarButtonItem * saveButton; - (IBAction) photoFromAlbum; - (IBAction) photoFromCamera; - (IBAction) saveImageToAlbum; - (IBAction) applyImageFilter: (id) Absender; @end @implementation ViewController @synthesize photoCarousel, filterButton, saveButton;

In Zeile 1 oben ersetzen selectedImageView Steckdose mit einem iCarousel Steckdose genannt photoCarousel. Tauschen Sie auch die Variablen in der synthesize-Anweisung in Zeile 14 oben aus.

Gehen Sie zurück zum Interface Builder und ziehen Sie einen neuen UIView auf den View Controller. Mit dem Neuen UIView Klicken Sie im Bereich "Dienstprogramme" auf die Registerkarte "Identitätsinspektor" und setzen Sie den Wert für das Feld "Klasse" auf "iCarousel". Dies teilt dem Interface Builder mit, dass die UIView Wir haben das Projekt hinzugefügt und sollten als Instanz der Instanz instanziiert werden iCarousel Klasse.

Stellen Sie jetzt eine Verbindung zwischen dem her photoCarousel Steckdose gerade erklärt und die UIView gerade als Unteransicht hinzugefügt.

Wir müssen sowohl die Datenquelle als auch den Delegaten für festlegen photoCarousel Dies ist auch in Interface Builder möglich. Gehen Sie zuerst zu ViewController.h und erklären, dass dieser View-Controller den entsprechenden Protokollen entspricht:

 #einführen  #import "iCarousel / iCarousel.h" @interface ViewController: UIViewController 

In Zeile 2 importieren wir iCarousel, und in Zeile 4 erklären wir die Konformität sowohl für den Delegierten als auch für die Datenquelle.

Zurück in der Storyboard-Datei können Sie jetzt sowohl die Datenquelle als auch den Delegaten dem View-Controller zuordnen.

Bevor Sie fortfahren, ändern Sie die Hintergrundfarbe des Fensters iCarousel Blick auf Schwarz.

Okay, noch eine Sache. Wir möchten, dass die iCarousel-Ansicht unterhalb von angezeigt wird UIToolbar in der Ansichtshierarchie. Sie können dies visuell tun, indem Sie sie einfach in die richtige Reihenfolge im Interface Builder ziehen:

Beachten Sie, wie die iCarousel-Ansicht jetzt vor der Symbolleiste angezeigt wird.

Speichern Sie Ihre Arbeit im Interface Builder.


Schritt 4: Implementieren Sie die iCarousel-Protokolle

iCarousel verwendet ein ähnliches Designmuster UITableView , dass eine Datenquelle verwendet wird, um Eingaben in das Steuerelement einzuspeisen, und ein Delegat wird verwendet, um die Interaktion mit dem Steuerelement abzuwickeln.

Für unser Projekt ist die Datenquelle eine einfache NSMutableArray "displayImages" genannt. Fügen Sie dies der Klassenerweiterung in hinzu ViewController.m jetzt:

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

Als nächstes möchten wir Speicher für das Array im angegebenen Initializer der Klasse zuordnen. In unserem Fall wird der View-Controller von einem Storyboard aus instanziiert, so dass der richtige Initialisierer vorhanden ist initWithCoder:. Wenn die Klasse jedoch programmgesteuert von einer XIB aus instanziiert werden soll, wäre dies der richtige Initialisierer initWithNibName: Bundle:. Um beiden Initialisierungsstilen Rechnung zu tragen, schreiben wir unseren eigenen benutzerdefinierten Initialisierer und rufen ihn von beiden auf:

 - (void) customSetup displayImages = [[NSMutableArray-Zuordnung] 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; 

Jetzt können wir die Datenquelle implementieren und delegieren. Beginnen Sie mit der Datenquellenmethode numberOfItemsInCarousel:, wie so:

 #pragma mark #pragma mark iCarousel DataSource / Delegate / Custom - (NSUInteger) numberOfItemsInCarousel: (iCarousel *) Karussell return [displayImages count]; 

Dies teilt iCarousel mit, wie viele Bilder angezeigt werden sollen, indem die Anzahl der im Datenquellenarray gespeicherten Bilder betrachtet wird.

Schreiben Sie als Nächstes die Methode, die für jedes im Karussell angezeigte Bild tatsächlich eine Ansicht generiert:

 - (UIView *) carousel: (iCarousel *) carousel viewForItemAtIndex: (NSUInteger) Index wiederverwendende Ansicht: (UIView *) view // Neue Ansicht erstellen, wenn keine Ansicht für das Recycling verfügbar ist (view == nil) view = [[UIImageView Alloc] initWithFrame: CGRectMake (0, 0, 300.0f, 300.0f)]; view.contentMode = UIViewContentModeCenter;  ((UIImageView *) - Ansicht) .image = [displayImages objectAtIndex: index]; Ansicht zurückkehren; 

Dies ist ein guter Anfang, aber es gibt ein sehr wichtiges Problem mit den oben genannten: Die Bilder sollten verkleinert werden, bevor sie an iCarousel geliefert werden. Fügen Sie die folgenden Codezeilen hinzu, um die Methode zu aktualisieren:

 - (UIView *) carousel: (iCarousel *) carousel viewForItemAtIndex: (NSUInteger) Index wiederverwendende Ansicht: (UIView *) view // Neue Ansicht erstellen, wenn keine Ansicht für das Recycling verfügbar ist (view == nil) view = [[UIImageView Alloc] initWithFrame: CGRectMake (0, 0, 300.0f, 300.0f)]; view.contentMode = UIViewContentModeCenter;  // Intelligente Skalierung auf maximal 250px Breite oder Höhe UIImage * originalImage = [displayImages objectAtIndex: index]; CGSize maxSize = CGSizeMake (250.0f, 250.0f); CGSize targetSize; // Wenn das Bild ein Querformat ist, die Breite auf 250px setzen und die Höhe dynamisch ermitteln, wenn (originalImage.size.width> = originalImage.size.height) float newHeightMultiplier = maxSize.width / originalImage.size.width; targetSize = CGSizeMake (maxSize.width, round (originalImage.size.height * newHeightMultiplier));  // Wenn das Bild Hochformat ist, setzen Sie die Höhe auf 250px und ermitteln Sie dynamisch die Breite else float newWidthMultiplier = maxSize.height / originalImage.size.height; targetSize = CGSizeMake (round (newWidthMultiplier * originalImage.size.width), maxSize.height);  // Verändern Sie die Größe des Quellbilds nach unten, um in die Ansicht "iCarousel ((UIImageView *))" zu passen. .Image = [[displayImages objectAtIndex: index] resizedImage: targetSize interpolationQuality: kCGInterpolationHigh]; Ansicht zurückkehren; 
Profi-Tipp: Verwenden Sie diese Methode in einer Produktions-App? Erwägen Sie, den Code für die Leistung zu verbessern, indem Sie die Bildgröße für einen Hintergrund-Thread ändern und ein separates NSMutableArray beibehalten, das die verkleinerten Bildversionen zwischenspeichert. UPDATE 27.09.2012: Nick Lockwood (Autor von iCarousel) hat ein Projekt namens FXImageView veröffentlicht, das das Laden von Bildern in einem Hintergrundthread automatisch behandelt. Es enthält auch andere nützliche Glocken und Pfeifen wie Schlagschatten und abgerundete Ecken!

Wir haben oben eine maximale Größe von 250px festgelegt entweder die Breite oder Höhe des Bildes, und dann skalieren wir das gegenüberliegende Attribut entsprechend. Dies beschränkt die Proportionen des Bildes und sieht viel schöner aus, als einfach auf ein 250 x 250 Pixel großes Quadrat zu skalieren.

Die beiden oben genannten Methoden sind alles, was iCarousel benötigt, um Bilder anzuzeigen.

Mit den konfigurierten Delegaten- und Datenquellenmethoden ist jetzt ein guter Zeitpunkt zum Einrichten des iCarousel-Objekts in der viewDidLoad Methode auch:

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

Mit nur wenigen weiteren Änderungen kann das Projekt Bilder innerhalb eines Karussells anzeigen!


Schritt 5: Wechseln Sie zum neuen Datenmodell

Am Anfang dieses Tutorials haben wir die ersetzt selectedImageView Eigenschaft mit der photoCarousel Eigenschaft aktualisiert, die Storyboard-Schnittstelle entsprechend aktualisiert und eine erstellt NSMutableArray als iCarousel-Datenmodell fungieren. Es gibt jedoch einige Methoden in ViewController.m Es wird immer noch das alte Datenmodell verwendet, durch das das Projekt nicht kompiliert werden kann. Aktualisieren Sie die saveImageToAlbum Methode wie folgt:

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

Zeile 3 wählt die UIImage aus dem Datenmodell, das dem aktuellen iCarousel-Index entspricht. Zeile 4 führt den eigentlichen Schreibvorgang mit diesem Image durch.

Gehen Sie als nächstes zum UIImagePickerController Methode delegieren und Code ändern:

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

In Zeile 6 oben fügen wir das ausgewählte Foto zum neuen Modell hinzu und in Zeile 8 erzwingen Sie eine Aktualisierung des Karussells.

Nur noch eine Änderung vorzunehmen. Gehen Sie zum Aktionsblatt clickedButtonAtIndex: Methode und ändern Sie den Code wie folgt:

 #pragma mark - #pragma mark UIActionSheetDelegate - (void) actionSheet: (UIActionSheet *) actionSheet clickedButtonAtIndex: (NSInteger) buttonIndex if (buttonIndex == actionSheet.cancelButtonIndex) return;  GPUImageFilter * selectedFilter; switch (buttonIndex) case 0: selectedFilter = [[GPUImageGrayscaleFilter-Zuordnung] init]; brechen; Fall 1: selectedFilter = [[GPUImageSepiaFilter-Zuordnung] init]; brechen; Fall 2: selectedFilter = [[GPUImageSketchFilter-Zuordnung] init]; brechen; Fall 3: selectedFilter = [[GPUImagePixellateFilter-Zuordnung] init]; brechen; Fall 4: selectedFilter = [[GPUImageColorInvertFilter Allocation] init]; brechen; Fall 5: selectedFilter = [[GPUImageToonFilter-Zuordnung] init]; brechen; Fall 6: selectedFilter = [[GPUImagePinchDistortionFilter-Zuordnung] init]; brechen; Fall 7: selectedFilter = [[GPUImageFilter-Zuordnung] init]; brechen; Standard: Pause;  UIImage * filtersImage = [selectedFilter imageByFilteringImage: [displayImages objectAtIndex: self.photoCarousel.currentItemIndex]]; [displayImages replaceObjectAtIndex: self.photoCarousel.currentItemIndex withObject: gefilterteImage]; [self.photoCarousel reloadData]; 

Die letzten drei Zeilen dieser Methode filtern das Datenmodellbild, das dem aktuellen Karussellindex entspricht, ersetzen die Karussellanzeige durch dieses Bild und aktualisieren das Karussell.

Wenn alles gut gelaufen ist, sollten Sie das Projekt jetzt kompilieren und ausführen können! Auf diese Weise können Sie Ihre Bilder innerhalb des Karussells und nicht nur in einer Bildansicht anzeigen.


Schritt 6: Fügen Sie eine Geste zum Löschen von Bildern hinzu

Die App sieht bisher gut aus, aber es wäre schön, wenn der Benutzer ein Foto aus dem Karussell entfernen könnte, nachdem er es dem Display hinzugefügt hat. Kein Problem! Wir können jeden auswählen UIGestureRecognizer Unterklasse, um dies zu ermöglichen. Für dieses Tutorial habe ich mich für ein Doppeltippen mit zwei Fingern entschieden. Diese Geste ist möglicherweise nicht sofort intuitiv, aber sie lässt sich leicht ausführen, und die zusätzliche Komplexität hilft, das versehentliche Entfernen von Bildern zu verhindern.

Innerhalb des ViewController.m Datei, gehe zum Karussell: viewForItemAtIndex: wiederverwendbarAnsicht: method und fügen Sie die folgenden Zeilen unmittelbar vor dem Ende der Methode hinzu:

 // Verändere die Größe des Quellbilds, damit es gut in die Ansicht iCarousel ((UIImageView *)) passt .image = [[displayImages objectAtIndex: index] resizedImage: targetSize interpolationQuality: kCGInterpolationHigh]; // Doppeltipp mit zwei Fingern löscht ein Bild UITapGestureRecognizer * gesture = [[UITapGestureRecognizer-Zuordnung] initWithTarget: Eigenaktion: @selector (removeImageFromCarousel :)]; gesture.numberOfTouchesRequired = 2; gesture.numberOfTapsRequired = 2; view.gestureRecognizers = [NSArray arrayWithObject: Geste]; Ansicht zurückkehren;

Zeilen 4 - 8 deklarieren ein neues UITapGestureRecognizer Setzen Sie die Anzahl der Berührungen (d. h. Finger), die zum Auslösen der Geste erforderlich sind, auf 2, und legen Sie auch die Anzahl der erforderlichen Taps auf 2 fest. Kurz bevor wir die Ansicht wieder an das iCarousel-Objekt übergeben, setzen wir das gestureRecognizers Eigenschaft mit dem neu gebildeten Erkenner.

Beachten Sie, dass diese Gestenerkennung beim Auslösen den Wahlschalter auslöst removeImageFromCarousel:. Lassen Sie uns das als nächstes implementieren:

 - (void) removeImageFromCarousel: (UIGestureRecognizer *) Geste [Geste removeTarget: Eigenaktion: @selector (removeImageFromCarousel :)]; [displayImages removeObjectAtIndex: self.photoCarousel.currentItemIndex]; [self.photoCarousel reloadData]; 

Zeile 3 entfernt die Geste vom aktuellen Ziel, um zu verhindern, dass während der Verarbeitung mehrere Gesten ausgelöst werden. Die verbleibenden zwei Zeilen sind an dieser Stelle nichts Neues.

Erstellen Sie die App erneut und führen Sie sie erneut aus. Sie sollten jetzt Objekte dynamisch aus dem Karussell entfernen können!


Schritt 7: Erstellen Sie einen MTCameraViewController

Der Rest dieses Tutorials konzentriert sich auf die Verwendung GPUImageStillCamera Erstellen eines benutzerdefinierten Kamera-Picker-Steuerelements, mit dem Filter auf den eingehenden Videostream in Echtzeit angewendet werden können. GPUImageStillCamera arbeitet eng mit einer Klasse namens GPUImageView. Kamerabilder generiert von GPUImageStillCamera werden an einen zugewiesenen gesendet GPUImageView Objekt zur Anzeige für den Benutzer. All dies wird mit der zugrundeliegenden Funktionalität erreicht, die von bereitgestellt wird AV-Gründung Framework, das programmgesteuerten Zugriff auf Kamera-Frame-Daten bietet.

weil GPUImageView ist eine Kinderklasse von UIView, Wir können das gesamte Kameradisplay in unsere eigene Umgebung einbetten UIViewController Klasse.

Neues hinzufügen UIViewController Klicken Sie mit der rechten Maustaste auf "PhotoFX" im Projektnavigator und wählen Sie dann aus Neue Datei> Objective-C-Klasse. Benennen Sie die Klasse "MTCameraViewController" und geben Sie "UIViewController" in das Feld "Subclass of" ein.

Klicken Sie auf "Weiter" und dann auf "Erstellen", um den Vorgang abzuschließen.

Gehe zum MTCameraViewController.m Datei und Import GPUImage:

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

Als Nächstes erstellen Sie eine Klassenerweiterung mit den erforderlichen GPUImage-Datenmitgliedern:

 @interface MTCameraViewController ()  GPUImageStillCamera * stillCamera; GPUImageFilter * filter;  @Ende

Zum Schluss gehe zum viewDidLoad: Methode und fügen Sie den Code hinzu, um die Kameraerfassung zu starten:

 - (void) viewDidLoad [super viewDidLoad]; // Erstes Kamerafilterfilter einrichten = [[GPUImageFilter-Zuordnung] init]; [filter preparForImageCapture]; GPUImageView * filterView = (GPUImageView *) self.view; [filter addTarget: filterView]; // Benutzerdefinierte GPUImage-Kamera erstellen stillCamera = [[GPUImageStillCamera-Zuordnung] init]; stillCamera.outputImageOrientation = UIInterfaceOrientationPortrait; [stillCamera addTarget: filter]; // Anzeige des Videokamera-Streams starten [stillCamera startCameraCapture]; 

Die Zeilen 5 - 9 erstellen ein neues GPUImageView für die Anzeige des Kameraeinzugs und einer Standardeinstellung GPUImageFilter Beispiel für das Anwenden eines speziellen Effekts auf die Ansicht. Wir hätten eine der beiden genauso gut benutzen können GPUImageFilter Unterklassen, wie z GPUImageSketchFilter, Stattdessen beginnen wir mit dem Standardfilter (d. h. ohne Manipulationen) und lassen den Benutzer später einen Filter dynamisch auswählen.

Die Zeilen 11 - 17 instanziieren die GPU-Kamerainstanz und wenden den zuvor erstellten Filter auf die Kamera an, bevor die Aufnahme gestartet wird.


Schritt 8: Fügen Sie die benutzerdefinierte Kamera in IB hinzu

Bevor der Code aus Schritt 8 funktioniert, müssen Sie den benutzerdefinierten Code hinzufügen MTCameraViewController Klasse, die gerade im Storyboard des Projekts erstellt wurde.

Öffne das MainStoryboard.storyboard Datei und ziehen Sie einen neuen View Controller aus der Objektbibliothek. Wechseln Sie bei ausgewähltem Objekt zur Registerkarte Identitätsinspektor und setzen Sie den Feldwert "Klasse" auf "MTCameraViewController"..

Ziehen Sie als nächstes eine UIToolbar Klicken Sie auf den Bildschirm, und legen Sie im Eigenschafteninspektor die Stileigenschaft auf "Black Opaque" fest. Fügen Sie der Symbolleiste dann zwei Schaltflächenelemente mit flexibler Breitenleiste mit einem "Foto aufnehmen" hinzu. UIBarButtonItem Im Zentrum.

Um diesen View Controller mit dem Anwendungsfluss zu verbinden, klicken Sie mit der rechten Maustaste auf die Schaltfläche "Kamera" im Haupt-View-Controller, und ziehen Sie den ausgelösten Segmentauslass auf den neuen View-Controller:

Wenn Sie dazu aufgefordert werden, wählen Sie als Trennstil "Push".

Wechseln Sie, während das neu hinzugefügte Segue-Objekt ausgewählt ist, zum "Attribute-Inspector" und setzen Sie den Bezeichner auf "pushMTCamera". Fahren Sie fort und stellen Sie sicher, dass "Push" aus der Dropdown-Liste "Style" ausgewählt ist.

Stellen Sie sicher, dass beim Erstellen des Segues die UIImagePicker wird nicht mehr angezeigt, wenn der Benutzer auf dem ersten Anwendungsbildschirm auf die Kamerataste tippt, indem Sie die Verbindung mit trennen IBAction ausgang von der photoFromCamera Methode.

Wählen Sie abschließend die Primäransicht des neu erstellten MTCameraViewControllers. Gehen Sie zum Identitätsinspektor und setzen Sie den Klassenwert auf "GPUImageView"..

Noch nicht perfekt. Wenn Sie jetzt die App erstellen und ausführen, sollten Sie Push ausführen können MTCameraViewController auf die Ansichtshierarchie und beobachten GPUImageView Zeigen Sie die Bilder der Kamera in Echtzeit an!


Schritt 9: Echtzeitfilterauswahl hinzufügen

Wir können jetzt die Logik hinzufügen, die zur Steuerung des auf die Kameraanzeige angewendeten Filters erforderlich ist. Gehen Sie zuerst zum viewDidLoad: Methode innerhalb der MTCameraViewController.m Datei und fügen Sie den Code hinzu, der eine Schaltfläche "Filter" oben rechts im View Controller erstellt:

 - (void) viewDidLoad [super viewDidLoad]; // Filter-Schaltfläche zur Schnittstelle hinzufügen UIBarButtonItem * filterButton = [[UIBarButtonItem-Zuordnung] initWithTitle: @ "Filter" -Stil: UIBarButtonItemStylePlain Ziel: Eigenaktion: @selector (applyImageFilter :)]; self.navigationItem.rightBarButtonItem = filterButton;

In Zeile 6 oben erstellen wir einen benutzerdefinierten Code UIBarButtonItem das wird auslösen applyImageFilter: wenn ausgewählt.

Erstellen Sie nun die Auswahlmethode:

 - (IBAction) applyImageFilter: (id) sender UIActionSheet * filterActionSheet = [[UIActionSheet-Zuordnung] initWithTitle: @ "Filter auswählen" delegate: self cancelButtonTitle: @ "Cancel" destructiveButtonTitles: @ "Skizze", @ "Pixellate", @ "Color Invert", @ "Toon", @ "Pinch Distort", @ "None", nil]; [filterActionSheet showFromBarButtonItem: Sender animiert: JA]; 

Nach dem Hinzufügen der obigen Informationen wird eine Warnmeldung angezeigt, die besagt, dass der aktuelle View-Controller nicht mit dem übereinstimmt UIActionSheetDelegate Protokoll. Beheben Sie das Problem jetzt, indem Sie zu gehen MTCameraViewController.h und die Klassendeklaration wie folgt ändern:

 #einführen  @interface MTCameraViewController: UIViewController  @Ende

Vervollständige den Kreis, indem du zurück zu den MTCameraViewController.m Datei und Hinzufügen der Logik, die auf die reagiert UIActionSheet vorgeführt:

 - (void) actionSheet: (UIActionSheet *) actionSheet clickedButtonAtIndex: (NSInteger) buttonIndex // Sichern, wenn die Schaltfläche "Abbrechen" betätigt wurde if (actionSheet.cancelButtonIndex == buttonIndex) return;  GPUImageFilter * selectedFilter; [stillCamera removeAllTargets]; [filter removeAllTargets]; switch (buttonIndex) case 0: selectedFilter = [[GPUImageGrayscaleFilter-Zuordnung] init]; brechen; Fall 1: selectedFilter = [[GPUImageSepiaFilter-Zuordnung] init]; brechen; Fall 2: selectedFilter = [[GPUImageSketchFilter-Zuordnung] init]; brechen; Fall 3: selectedFilter = [[GPUImagePixellateFilter-Zuordnung] init]; brechen; Fall 4: selectedFilter = [[GPUImageColorInvertFilter Allocation] init]; brechen; Fall 5: selectedFilter = [[GPUImageToonFilter-Zuordnung] init]; brechen; Fall 6: selectedFilter = [[GPUImagePinchDistortionFilter-Zuordnung] init]; brechen; Fall 7: selectedFilter = [[GPUImageFilter-Zuordnung] init]; brechen; Standard: Pause;  filter = selectedFilter; GPUImageView * filterView = (GPUImageView *) self.view; [filter addTarget: filterView]; [stillCamera addTarget: filter]; 

Die Zeilen 11-12 werden verwendet, um den aktuell ausgewählten Filter zurückzusetzen.

Die Zeilen 15 - 42 oben sollten der Logik in vertraut sein ViewController.m; Wir aktivieren nur die ausgewählte Schaltfläche, um eine Instanz des Korrelationsfilters zu erstellen.

Die Zeilen 44 - 47 nehmen den neu erstellten Filter und wenden ihn auf die GPUImage-Kamera an.

Wenn Sie das Projekt jetzt erstellen und ausführen, sollten Sie sehen, dass der Benutzer mit der neu erstellten Filterschaltfläche GPUImage-Filter in Echtzeit ausprobieren kann!


Schritt 10: Erstellen Sie ein Kamera-Delegatenprotokoll

Nachdem nun die Live-Feed-Filter aktiv sind, besteht der letzte große Schritt in diesem Lernprogramm darin, dem Benutzer zu ermöglichen, Schnappschüsse mit der GPUImage-Kamera zu erstellen und sie anschließend im Fotokarussell des Hauptansicht-Controllers anzuzeigen.

Um dies zu erreichen, übergeben wir Nachrichten zwischen Ansichtssteuerungen unter Verwendung des Delegierungsentwurfsmusters. Insbesondere erstellen wir unser eigenes Protokoll für die formale Delegierung MTCameraViewController und konfigurieren Sie dann das Hauptfenster ViewController Klasse, die diesem Protokoll entspricht, um Delegierungsnachrichten zu empfangen.

Um zu beginnen, gehe zu MTViewController.h und ändern Sie den Code wie folgt:

 #einführen  @protocol MTCameraViewControllerDelegate - (void) didSelectStillImage: (NSData *) - Image withError: (NSError *) - Fehler; @end @interface MTCameraViewController: UIViewController @property (schwach, nicht atomar) ID-Delegierter; @Ende

Der obige Code deklariert ein formelles Delegatenmuster, das aufgerufen wird MTCameraViewControllerDelegate in den Zeilen 3-7 und erstellt dann ein Delegate-Objekt in Zeile 11.

Als nächstes wechseln Sie zu MTCameraViewController.m und synthetisieren Sie die Delegate-Eigenschaft:

 @implementation MTCameraViewController @synthesize delegate;

Wenn das Protokoll deklariert ist, müssen wir es jetzt in der Hauptsache implementieren ViewController Klasse. Gehe zu ViewController.h und füge folgende Zeilen hinzu:

 #einführen  #import "iCarousel.h" #import "MTCameraViewController.h" @interface ViewController: UIViewController  @Ende

Jetzt mach das auf ViewController.m Datei. Wir möchten die Delegate-Eigenschaft zuweisen, wenn der View-Controller instanziiert wird. Da wir Storyboards verwenden, ist der richtige Ort dafür prepareForSegue: Absender: Methode, die aufgerufen wird, bevor der neue View-Controller auf den Bildschirm gedrückt wird:

 - (void) preparForSegue: (UIStoryboardSegue *) Absender Absender: (ID) Absender if ([segue.identifier isEqualToString: @ "pushMTCamera"]) // Legt den Delegaten so fest, dass dieser Controller aufgenommene Fotos empfangen kann. MTCameraViewController * cameraViewController = (MTCameraViewController) *) segue.destinationViewController; cameraViewController.delegate = self; 

Als nächstes müssen wir das implementieren didSelectStillImage: withError: Methode für die MTCameraViewControllerDelegate Protokoll:

 #pragma mark - #pragma mark MTCameraViewController // Diese Delegat-Methode wird aufgerufen, nachdem unsere benutzerdefinierte Kameraklasse ein Foto aufgenommen hat. (void) didSelectStillImage: (NSData *) imageData withError: (NSError *) -Fehler if (! error) UIImage * image = [[UIImage allocation] initWithData: imageData]; [displayImages addObject: image]; [self.photoCarousel reloadData]; self.filterButton.enabled = YES; self.saveButton.enabled = YES;  else UIAlertView * alert = [[UIAlertView-Zuordnung] initWithTitle: @ "Capture Error" -Meldung: @ "Foto kann nicht aufgenommen werden." Delegat: nil cancelButtonTitle: @ "OK" otherButtonTitles: nil]; [Alertshow]; 

Der obige Code konvertiert das NSData Objekt an die Methode übergeben a UIImage und laden Sie dann das Fotokarussell erneut.

Zum Schluss müssen wir die Dinge abschließen, indem wir zu zurückkehren MTCameraViewController.m und Hinzufügen im entsprechenden Delegate-Methodenaufruf. Richten Sie zuerst ein ein IBAction Methode, die einen Kamera-Snap auslöst:

 GPUImageFilter * filter;  - (IBAction) captureImage: (id) Sender; @Ende

Vor dem Fortfahren, Verbinden Sie diese Methode mit der Schaltfläche "Foto aufnehmen" im MainStoryboard.storyboard Datei.

Fügen Sie schließlich die Implementierung der Methode hinzu:

 -(IBAction) captureImage: (id) sender // Deaktivieren, um mehrere Taps während der Verarbeitung zu verhindern. UIButton * captureButton = (UIButton *) sender; captu