Mejorando una aplicación de fotos con GPUImage & iCarousel

Este tutorial le enseñará cómo usar GPUImage para aplicar filtros de imagen en tiempo real a medida que se muestra la alimentación de la cámara del dispositivo. A lo largo del camino, aprenderá cómo rellenar automáticamente las imágenes dentro de un controlador de carrusel y cómo cambiar el tamaño de las imágenes con UIImage + Categories.


Descripción del proyecto


Requisitos previos del tutorial

Este tutorial se basa en una publicación anterior titulada "Crear una aplicación de fotos con GPUImage". La lección anterior demostró cómo usar UIImagePickerController para seleccionar fotos del álbum de fotos o la cámara del dispositivo, cómo agregar el GPUImage biblioteca a su proyecto, y cómo utilizar el GPUImageFilter Clase para mejorar los fotogramas de la cámara. Si ya estás familiarizado con UIImagePickerController y puede averiguar cómo agregar GPUImage a su proyecto por su cuenta, debería poder continuar desde donde quedó el último tutorial sin problemas.


Paso 1: Importar iCarousel

Este proyecto hará un uso extensivo de un proyecto de código abierto llamado iCarousel para agregar una visualización elegante de las fotos seleccionadas..

Para incluir iCarousel en su proyecto, vaya a la página oficial de GitHub y descargue el código fuente como un archivo zip. Extraiga el código del archivo ZIP y luego arrastre y suelte la carpeta titulada "iCarousel" en Xcode Project Navigator. Esta carpeta debe contener tanto iCarousel.h y iCarousel.m. Asegúrese de seleccionar "Crear grupos para cualquier carpeta agregada" y marque la casilla junto a "Copiar elementos en la carpeta del grupo de destino (si es necesario)", así como la casilla junto al nombre de destino de su proyecto en el área "Agregar a objetivos".

Siguiente ve a ViewController.m y agrega una declaración de importación para iCarousel:

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

Paso 2: Importar UIImage + Categorías

Antes de mostrar nuestras imágenes con iCarousel, tendremos que escalarlas a un tamaño aceptable. En lugar de escribir todo el código para hacer esto a mano, usaremos el excelente proyecto UIImage + Categories, que proporciona una funcionalidad básica para cambiar el tamaño de las imágenes, así como algunos otros trucos de manipulación de imágenes..

Propina: También puede utilizar el proyecto MGImageUtilities para esta tarea. Si bien los detalles de la implementación diferirán ligeramente, también proporciona un excelente soporte para el escalamiento de UIImage.

Descargar el UIImage + Categorías código de GitHub y luego crear un nuevo grupo con el mismo nombre dentro de Xcode. Arrastre los archivos de implementación y de encabezado para UIImage + Alpha, UIImage + Redimensionar, y UIImage + RoundedCorner en su proyecto. Asegúrese de seleccionar "Crear grupos para cualquier carpeta agregada" y marque la casilla junto a "Copiar elementos en la carpeta del grupo de destino (si es necesario)", así como la casilla junto al nombre de destino de su proyecto en el área "Agregar a objetivos".

Dentro de ViewController.m archivo, importar las categorías de imagen con la siguiente línea de código:

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

Paso 3: agregar la vista de iCarousel en IB

Con el código de iCarousel importado en nuestro proyecto, cambie a MainStoryboard.storyboard archivo para volver a trabajar nuestra interfaz.

Primero, seleccione el actual UIImageView Conectado al selectedImageView IBOutlet y eliminarlo. Cambie de nuevo a ViewController.m y modifique el código del proyecto para que lea como sigue:

 @ propiedad (no atómica, débil) IBOutlet iCarousel * photoCarousel; @ propiedad (no atómica, débil) IBOutlet UIBarButtonItem * filterButton; @ propiedad (no atómica, débil) IBOutlet UIBarButtonItem * saveButton; - (IBAction) photoFromAlbum; - (IBAction) photoFromCamera; - (IBAction) saveImageToAlbum; - (IBAction) applyImageFilter: (id) remitente; @end @implementation ViewController @synthesize photoCarousel, filterButton, saveButton;

En la línea 1 anterior, reemplace el selectedImageView salida con un iCarousel salida llamada carrusel de fotos. Cambie las variables en la declaración de síntesis en la línea 14 de arriba también.

Vuelve a Interface Builder y arrastra un nuevo Vista en el controlador de vista. Con el nuevo Vista seleccionado, vaya a la pestaña "Inspector de identidad" dentro del panel Utilidades y configure el valor del campo "Clase" en "iCarousel". Esto le dice a Interface Builder que Vista que agregamos al proyecto debe ser instanciado como una instancia de la iCarousel clase.

Ahora haz una conexión entre el carrusel de fotos salida acaba de declarar y la Vista acaba de añadir como una subvista.

Necesitamos establecer la fuente de datos y delegar para carrusel de fotos también, y podemos lograr esto desde dentro de Interface Builder. Primero, ve a ViewController.h y declare que este controlador de vista se ajustará a los protocolos apropiados:

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

En la línea 2 importamos iCarousel, y en la línea 4 declaramos conformidad tanto al delegado como a la fuente de datos.

De vuelta en el archivo del guión gráfico, ahora puede asignar el origen de datos y el delegado al controlador de vista.

Antes de continuar, siga adelante y cambie el color de fondo del iCarousel vista a negro.

Está bien, sólo una cosa más. Queremos que la vista de iCarousel aparezca debajo de UIToolbar en la jerarquía de vistas. Puede hacerlo visualmente simplemente arrastrándolos al orden correcto en el Creador de interfaces:

Observe cómo aparece ahora la vista de iCarousel antes de la barra de herramientas..

Guarda tu trabajo en Interface Builder.


Paso 4: Implementar los Protocolos de iCarousel

iCarousel utiliza un patrón de diseño similar a UITableView en la medida en que se utiliza una fuente de datos para introducir información en el control y un delegado se utiliza para manejar la interacción con el control.

Para nuestro proyecto, la fuente de datos será un simple NSMutableArray llamado "displayImages". Agregue esto a la extensión de clase en ViewController.m ahora:

 #import "UIImage + Resize.h" @interface ViewController () NSMutableArray * displayImages;  @ propiedad (no atómica, débil) IBOutlet iCarousel * photoCarousel;

A continuación, queremos asignar memoria para la matriz en el inicializador designado de la clase. En nuestro caso, el controlador de vista se creará una instancia desde un Storyboard, por lo que el inicializador adecuado es initWithCoder:. Sin embargo, si la clase se creara de manera programática desde un XIB, el inicializador adecuado sería initWithNibName: paquete:. Para acomodar cualquiera de los estilos de inicialización, escribiremos nuestro propio inicializador personalizado y lo llamaremos desde ambos, así:

 - (void) customSetup displayImages = [[NSMutableArray alloc] init];  - (id) initWithNibName: (NSString *) nibNameOrNil bundle: (NSBundle *) nibBundleOrNil if ((self = [super initWithNibName: nibNameOrNil bundle: nibBundleOrNil)) [self customSetup];  devuélvete a ti mismo;  - (id) initWithCoder: (NSCoder *) aDecoder if ((self = [super initWithCoder: aDecoder])) [self customSetup];  devuélvete a ti mismo; 

Ahora podemos implementar la fuente de datos y delegar. Comience con el método de fuente de datos numberOfItemsInCarousel:, al igual que:

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

Esto le dirá a iCarousel cuántas imágenes mostrar al mirar la cantidad de imágenes almacenadas en la matriz de origen de datos.

A continuación, escriba el método que realmente generará una vista para cada imagen mostrada en el carrusel:

 - (UIView *) carrusel: (iCarousel *) carousel viewForItemAtIndex: (NSUInteger) index reusingView: (UIView *) view // Crear nueva vista si no hay una vista disponible para su reciclaje si (view == nil) view = [[UIImageView alloc] initWithFrame: CGRectMake (0, 0, 300.0f, 300.0f)]; view.contentMode = UIViewContentModeCenter;  (Vista (UIImageView *)) .image = [displayImages objectAtIndex: index]; vista de vuelta 

Este es un buen comienzo, pero hay un problema muy importante con lo anterior: las imágenes deben reducirse antes de suministrarse a iCarousel. Agregue las siguientes líneas de código para actualizar el método:

 - (UIView *) carrusel: (iCarousel *) carousel viewForItemAtIndex: (NSUInteger) index reusingView: (UIView *) view // Crear nueva vista si no hay una vista disponible para su reciclaje si (view == nil) view = [[UIImageView alloc] initWithFrame: CGRectMake (0, 0, 300.0f, 300.0f)]; view.contentMode = UIViewContentModeCenter;  // Escala de forma inteligente hasta un máximo de 250 px en ancho o alto UIImage * originalImage = [displayImages objectAtIndex: index]; CGSize maxSize = CGSizeMake (250.0f, 250.0f); CGSize targetSize; // Si la imagen es horizontal, establezca el ancho en 250 px y calcule dinámicamente la altura si (originalImage.size.width> = originalImage.size.height) float newHeightMultiplier = maxSize.width / originalImage.size.width; targetSize = CGSizeMake (maxSize.width, round (originalImage.size.height * newHeightMultiplier));  // Si la imagen es vertical, establezca la altura en 250 px y determine dinámicamente el ancho else else float newWidthMultiplier = maxSize.height / originalImage.size.height; targetSize = CGSizeMake (round (newWidthMultiplier * originalImage.size.width), maxSize.height);  // Cambia el tamaño de la imagen de origen hacia abajo para que se ajuste bien a la vista de iCarousel ((UIImageView *)) .image = [[displayImages objectAtIndex: index] resizedImage: targetSize interpolationQuality: kCGInterpolationHigh]; vista de vuelta 
Pro Consejo: ¿Usando este método en una aplicación de producción? Considere mejorar el código de rendimiento haciendo el cambio de tamaño de la imagen en un hilo de fondo y manteniendo un NSMutableArray separado que almacena en caché las versiones de imagen reducidas.. ACTUALIZACIÓN 27/09/2012: Nick Lockwood (autor de iCarousel) ha lanzado un proyecto llamado FXImageView que manejará automáticamente la carga de imágenes en un hilo de fondo. También viene con otras campanas y silbidos útiles como sombras y esquinas redondeadas, así que échale un vistazo!

Arriba, establecemos un tamaño máximo de 250px para ya sea el ancho o el alto de la imagen, y luego reducimos la escala del atributo opuesto para coincidir. Esto restringe las proporciones de la imagen y se ve mucho mejor que simplemente reduciendo a un cuadrado de 250px por 250px.

Los dos métodos anteriores son todos los que iCarousel necesita para comenzar a mostrar imágenes.

Con los métodos de fuente de datos y delegado configurados, ahora es un buen momento para configurar el objeto iCarousel en el viewDidLoad método también:

 - (void) viewDidLoad [super viewDidLoad]; // Configuración de iCarousel self.photoCarousel.type = iCarouselTypeCoverFlow2; self.photoCarousel.bounces = NO; 

Con solo unos pocos ajustes más, el proyecto podrá mostrar imágenes dentro de un carrusel.!


Paso 5: Cambiar al nuevo modelo de datos

Anteriormente en este tutorial, reemplazamos el selectedImageView propiedad con el carrusel de fotos propiedad, actualizó la interfaz del guión gráfico para que coincida y creó un NSMutableArray Actuar como el modelo de datos de iCarousel. Sin embargo, hay algunos métodos en ViewController.m Todavía utilizamos el modelo de datos anterior que evitará que el proyecto se compile, así que vamos a solucionarlo ahora. Actualizar el saveImageToAlbum método como tal:

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

La línea 3 selecciona la UIImage del modelo de datos que coincide con el índice actual de iCarousel. La línea 4 realiza la escritura del disco real con esa imagen.

A continuación, vaya a la UIImagePickerController Delegar método y modificar el código:

 - (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 complete: NULL]; 

En la línea 6 anterior, agregamos la foto seleccionada al nuevo modelo y en la línea 8 forzamos una actualización del carrusel.

Sólo un cambio más para hacer. Ir a la hoja de acción clickedButtonAtIndex: Método y modificar el código de la siguiente manera:

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

Las tres últimas líneas de este método filtrarán la imagen del modelo de datos que corresponde al índice del carrusel actual, reemplazarán la pantalla del carrusel con esa imagen y luego actualizarán el carrusel.

Si todo salió bien, ¡ahora debería poder compilar y ejecutar el proyecto! Si lo hace, le permitirá ver sus imágenes dentro del carrusel en lugar de simplemente dentro de una vista de imagen.


Paso 6: Agrega un gesto para borrar imágenes

La aplicación se ve bien hasta ahora, pero sería bueno si el usuario pudiera eliminar una foto del carrusel después de agregarla a la pantalla. ¡No hay problema! Podemos seleccionar cualquier UIGestureRecognizer subclase para que esto suceda. Para este tutorial, he elegido usar un doble toque con dos dedos. Es posible que este gesto no sea intuitivo de inmediato, pero es fácil de realizar y la complejidad adicional ayudará a evitar la eliminación accidental de imágenes.

Dentro de ViewController.m archivo, vaya a la carrusel: viewForItemAtIndex: reusingView: Método y agregue las siguientes líneas justo antes del final del método:

 // Cambie el tamaño de la imagen de origen para que se ajuste a la vista de iCarousel ((UIImageView *)) .image = [[displayImages objectAtIndex: index] resizedImage: targetSize interpolationQuality: kCGInterpolationHigh]; // La doble pulsación de dos dedos eliminará una imagen UITapGestureRecognizer * gesto = [[UITapGestureRecognizer alloc] initWithTarget: self action: @selector (removeImageFromCarousel :)]; gesture.numberOfTouchesRequired = 2; gesto.numeroOfTapsRequired = 2; view.gestureRecognizers = [NSArray arrayWithObject: gesto]; vista de vuelta

Líneas 4 - 8 declaran un nuevo. UITapGestureRecognizer objeto, establezca el número de toques (es decir, los dedos) necesarios para activar el gesto en 2, y establezca el número de toques requeridos en 2 también. Finalmente, justo antes de devolver la vista al objeto iCarousel, configuramos gestoRecognizantes propiedad con el reconocedor recién formado.

Tenga en cuenta que cuando se active, este gestor de gestos activará el selector. removeImageFromCarousel:. Vamos a implementar eso a continuación:

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

La línea 3 elimina el gesto del objetivo actual para evitar que se activen múltiples gestos mientras se procesa. Las dos líneas restantes no son nada nuevo en este punto..

Construye y ejecuta la aplicación de nuevo. Ahora deberías poder eliminar dinámicamente artículos del carrusel!


Paso 7: Crear MTCameraViewController

El resto de este tutorial se centrará en el uso de GPUImageStillCamera para crear un control de selección de cámara personalizado que pueda aplicar filtros al flujo de video entrante en tiempo real. GPUImageStillCamera Trabaja estrechamente con una clase llamada. GPUImageView. Marcos de cámara generados por GPUImageStillCamera se envían a un asignado GPUImageView Objeto para mostrar al usuario. Todo esto se logra con la funcionalidad subyacente proporcionada por el Fundación Marco, que proporciona acceso programático a los datos del marco de la cámara..

Porque GPUImageView es una clase infantil de Vista, Podemos incrustar toda la pantalla de la cámara en nuestra propia costumbre. UIViewController clase.

Añadir un nuevo UIViewController Subclase al proyecto haciendo clic con el botón derecho en "PhotoFX" en el navegador del proyecto, y luego seleccionando Nuevo archivo> Clase Objective-C. Nombre la clase "MTCameraViewController" e ingrese "UIViewController" en el campo "subclase de".

Haga clic en "Siguiente" y luego "Crear" para completar el proceso.

Ve a la MTCameraViewController.m Archivo e importación GPUImage:

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

A continuación, cree una extensión de clase con los miembros de datos GPUImage necesarios:

 @interface MTCameraViewController ()  GPUImageStillCamera * stillCamera; Filtro GPUImageFilter *;  @final

Finalmente, ve a la viewDidLoad: Método y agregar el código para iniciar la captura de la cámara:

 - (void) viewDidLoad [super viewDidLoad]; // Configurar filtro de filtro de cámara inicial = [[GPUImageFilter alloc] init]; [filtro prepareForImageCapture]; GPUImageView * filterView = (GPUImageView *) self.view; [filter addTarget: filterView]; // Crear una cámara GPUImage personalizada stillCamera = [[GPUImageStillCamera alloc] init]; stillCamera.outputImageOrientation = UIInterfaceOrientationPortrait; [stillCamera addTarget: filter]; // Comenzar a mostrar la transmisión de la cámara de video [stillCamera startCameraCapture]; 

Las líneas 5 - 9 crean una nueva. GPUImageView para mostrar la alimentación de la cámara y un valor predeterminado GPUImageFilter Instancia para aplicar un efecto especial a la vista. Podríamos haber utilizado uno de los GPUImageFilter subclases, tales como GPUImageSketchFilter, pero comenzaremos con el filtro predeterminado (es decir, sin manipulaciones) y permitiremos que el usuario seleccione un filtro de forma dinámica más adelante.

Las líneas 11 a 17 crean una instancia de la instancia de la cámara GPU y aplican el filtro creado previamente a la cámara antes de iniciar la captura.


Paso 8: Agrega la cámara personalizada en IB

Antes de que el código del Paso 8 funcione, debemos agregar el código personalizado. MTCameraViewController Clase recién creada para el storyboard del proyecto..

Abre el MainStoryboard.storyboard Archivo y arrastre un nuevo controlador de vista desde la biblioteca de objetos. Con este objeto seleccionado, vaya a la pestaña del inspector de identidad y establezca el valor del campo "Clase" en "MTCameraViewController".

A continuación, arrastre un UIToolbar en la pantalla y establezca su propiedad de estilo en "Opaco Negro" en el inspector de Atributos. Luego, agregue dos elementos de botón de barra de ancho flexible a la barra de herramientas con un "Tomar foto" UIBarButtonItem en el centro.

Para conectar este controlador de vista al flujo de la aplicación, haga clic con el botón derecho en el botón "cámara" del controlador de vista principal y arrastre la salida de segmentos activada al nuevo controlador de vista:

Cuando se le solicite, seleccione "Push" como el estilo segue.

Con el objeto Segue recién agregado aún seleccionado, vaya al "inspector de atributos" y configure el identificador a "pushMTCamera". Continúe y asegúrese de que "Push" esté seleccionado en el menú desplegable "Estilo"..

Con el segue creado, asegúrese de que el UIImagePicker ya no se mostrará cuando el usuario toque el botón de la cámara en la primera pantalla de la aplicación al desconectar la IBAcción salida de la foto de la camara método.

Finalmente, seleccione la vista principal del MTCameraViewController recién creado. Vaya al inspector de identidad y establezca el valor de clase en "GPUImageView".

Aunque todavía no es perfecto, si compila y ejecuta la aplicación ahora, debería poder presionar MTCameraViewController en la jerarquía de vista y ver GPUImageView Muestra los cuadros de la cámara en tiempo real.!


Paso 9: Agregar selección de filtro en tiempo real

Ahora podemos agregar la lógica necesaria para controlar el filtro aplicado a la pantalla de la cámara. Primero, ve a la viewDidLoad: método dentro del MTCameraViewController.m Archivo y agregue el código que creará un botón "Filtro" en la parte superior derecha del controlador de vista:

 - (void) viewDidLoad [super viewDidLoad]; // Agregar botón de filtro a la interfaz UIBarButtonItem * filterButton = [[UIBarButtonItem alloc] initWithTitle: @ estilo "Filter": UIBarButtonItemStylePlain target: self action: @selector (applyImageFilter :)]; self.navigationItem.rightBarButtonItem = filterButton;

En la línea 6 anterior, creamos una personalizada. UIBarButtonItem que disparará applyImageFilter: cuando se selecciona.

Ahora crea el método selector:

 - (IBAction) applyImageFilter: (id) sender UIActionSheet * filterActionSheet = [[UIActionSheet alloc] initWithTitle: @ "Select Filter" delegate: self cancelBittictontitleititle: @ "Cancel" destructiveButtonTitle: nil otherparcitytontontontontontontontontontontontontontontontontontontontontontontontontontontontontontontontontitleittitle: @ "Cancel" @ "Sketch", @ "Pixellate", @ "Invertir color", @ "Toon", @ "Pinch Distort", @ "Ninguno", nil]; [filterActionSheet showFromBarButtonItem: sender animated: YES]; 

Después de agregar lo anterior, verá una advertencia del compilador que indica que el controlador de vista actual no se ajusta a la UIActionSheetDelegate protocolo. Solucione ese problema ahora yendo a MTCameraViewController.h y modificando la declaración de clase como tal:

 #importar  @interface MTCameraViewController: UIViewController  @fin

Completa el círculo volviendo a la MTCameraViewController.m archivo y añadiendo la lógica que responderá a la UIActionSheet presentado:

 - (void) actionSheet: (UIActionSheet *) actionSheet clickedButtonAtIndex: (NSInteger) buttonIndex // Bail si se pulsó el botón de cancelar si (actionSheet.cancelButtonIndex == buttonIndex) return;  GPUImageFilter * selectedFilter; [stillCamera removeAllTargets]; [filtro removeAllTargets]; switch (buttonIndex) caso 0: selectedFilter = [[GPUImageGrayscaleFilter alloc] init]; descanso; caso 1: selectedFilter = [[GPUImageSepiaFilter alloc] init]; descanso; caso 2: selectedFilter = [[GPUImageSketchFilter alloc] init]; descanso; caso 3: selectedFilter = [[GPUImagePixellateFilter alloc] init]; descanso; caso 4: selectedFilter = [[GPUImageColorInvertFilter alloc] init]; descanso; caso 5: selectedFilter = [[GPUImageToonFilter alloc] init]; descanso; caso 6: selectedFilter = [[GPUImagePinchDistortionFilter alloc] init]; descanso; caso 7: selectedFilter = [[GPUImageFilter alloc] init]; descanso; por defecto: break;  filtro = seleccionadoFiltro; GPUImageView * filterView = (GPUImageView *) self.view; [filter addTarget: filterView]; [stillCamera addTarget: filter]; 

Las líneas 11-12 se utilizan para restablecer el filtro seleccionado actualmente.

Las líneas 15 a 42 anteriores deben ser familiares a la lógica en ViewController.m; solo estamos activando el botón seleccionado para crear una instancia del filtro de correlación.

Las líneas 44 a 47 toman el filtro recién creado y lo aplican a la cámara GPUImage.

Si crea y ejecuta el proyecto ahora, debería ver que el botón de filtro recién creado le permite al usuario probar los filtros GPUImage en tiempo real!


Paso 10: Crear un protocolo de delegado de cámara

Ahora que los filtros de alimentación en vivo están funcionando, el último paso importante del tutorial es permitir al usuario tomar instantáneas con la cámara GPUImage y luego mostrarlas nuevamente en el carrusel de fotos del controlador de la vista principal.

Para lograr esto, pasaremos mensajes entre los controladores de vista utilizando el patrón de diseño de delegación. Específicamente, crearemos nuestro propio protocolo de delegado formal personalizado en MTCameraViewController y luego configurar la principal ViewController Clase para cumplir con ese protocolo para recibir mensajes de delegación..

Para empezar, ve a MTViewController.h y modifique el código de la siguiente manera:

 #importar  @protocol MTCameraViewControllerDelegate - (void) didSelectStillImage: (NSData *) imagen con error: (NSError *) error; @end @interface MTCameraViewController: UIViewController @property (débil, no atómico) delegado de identificación; @fin

El código anterior declara un patrón de delegado formal llamado MTCameraViewControllerDelegate en las líneas 3-7, y luego crea un objeto delegado en la línea 11.

Siguiente cambio a MTCameraViewController.m y sintetizar la propiedad delegada:

 @implementation MTCameraViewController @synthesize delegate;

Con el protocolo declarado, ahora necesitamos implementarlo en lo principal ViewController clase. Ir ViewController.h Y añada las siguientes líneas:

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

Ahora abre el ViewController.m expediente. Queremos asignar la propiedad delegada cuando se crea una instancia del controlador de vista. Debido a que estamos utilizando Storyboards, el lugar adecuado para hacerlo es en el prepareForSegue: remitente: Método, que se llamará justo antes de que el nuevo controlador de vista se introduzca en la pantalla:

 - (void) prepareForSegue: (UIStoryboardSegue *) segue sender: (id) sender if ([segue.identifier isEqualToString: @ "pushMTCamera"]) // Establezca el delegado para que este controlador pueda recibir las fotos conectadas. *) segue.destinationViewController; cameraViewController.delegate = self; 

A continuación necesitamos implementar el didSelectStillImage: withError: método requerido por el MTCameraViewControllerDelegate protocolo:

 #pragma mark - #pragma mark MTCameraViewController // Se llama a este método delegado después de que nuestra clase de cámara personalizada tome una foto - (nula) 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: @ Mensaje "Error de captura": @ "No se puede capturar la foto". delegate: nil cancelButtonTitle: @ "OK" otherButtonTitles: nil]; [espectáculo de alerta]; 

El código anterior convertirá el NSData objeto entregado al método a una UIImage y luego recargar el carrusel de fotos.

Por último, tenemos que envolver las cosas volviendo a MTCameraViewController.m y añadiendo la llamada de método de delegado apropiado. Primero, configura un IBAcción Método que activará un snap de cámara:

 Filtro GPUImageFilter *;  - (IBAction) captureImage: (id) remitente; @fin

Antes de continuar, Conecte este método al botón "Tomar foto" en el MainStoryboard.storyboard expediente.

Finalmente, agregue el método de implementación:

 -(IBAction) captureImage: (id) sender // Deshabilitar para evitar múltiples toques mientras se procesa UIButton * captureButton = (UIButton *) sender; captureButton.enabled = NO; // Imagen instantánea de la cámara GPU, enviar de vuelta al controlador de vista principal [stillCamera capturePhotoAsJPEGProcessedUpToFilter: filtrar conCompletionHandler: ^ (NSData * procesadoJPEG, NSError * error) if ([delegate responsToSelector: (selSelectStillImage: self.delegate didSelectStillImage: processingJPEG withError: error];  else NSLog (@ "El delegado no respondió al mensaje");  runOnMainQueueWithoutDeadlocking (^ [self.navigationController popToRootViewControllerAnimated: YES];); ]; 

Las líneas 3-5 anteriores desactivan el botón "Tomar foto" para evitar varias pulsaciones durante el procesamiento.

Las líneas 7 a 22 usan el método GPUImage. capturePhotoAsJPEGProcessedUpToFilter: withCompletionHandler: para guardar realmente una imagen JPEG, verifique si se ha configurado un delegado y luego envíe los datos de la imagen al delegado si está configurado.

La línea 19 muestra el controlador de vista actual, pero lo hace en el h