Aggiunta di effetti di sfocatura su iOS

introduzione

Con iOS 7, abbiamo assistito a un cambiamento nel paradigma di design di Apple per i dispositivi mobili. Non solo hanno adottato il cosiddetto design piatto, Apple ha anche aggiunto alcuni elementi a questo modello. Una di queste aggiunte è l'uso di sfondi sfocati-traslucidi per trasmettere la nozione di profondità e contesto. Prendere Centro di controllo per esempio, offusca il contenuto della vista dietro di esso mentre viene tirato su. Questo dà all'utente la sensazione che sia posizionato sopra altri contenuti sullo schermo e merita attenzione. Lo fa senza che l'utente perda traccia di dove si trova nell'app.

Anche se la sfocatura e la traslucenza sono utilizzate in tutto il sistema operativo su iOS 7, l'SDK di iOS non ci fornisce alcuna API per ottenere un effetto simile. In questo tutorial ti illustrerò alcuni metodi per creare visualizzazioni sfocate creando l'app di esempio mostrata di seguito.

La nostra app di esempio avrà una vista in basso, che può essere rivelata tirandola verso l'alto. La vista è traslucida e sfoca il contenuto sottostante nella gerarchia della vista, simile a Control Center su iOS 7.

1. Impostazione del progetto

Panoramica

L'app che stiamo per costruire mostrerà una foto insieme al nome e all'autore della foto, visualizzati in un cerchio bianco. Ci sarà anche una piccola vista rettangolare nella parte inferiore dello schermo che sfoca la foto e che può essere tirato su per visualizzare ulteriori informazioni sulla foto.

Presumo che tu sappia già come lavorare con elementi di base dell'interfaccia utente, come viste, visualizzazioni di immagini e viste di scorrimento. In questo tutorial, ci concentreremo sulla gerarchia delle viste e sulle viste di cui abbiamo bisogno per creare l'effetto sfocatura.

Dai un'occhiata all'immagine qui sopra. Separa la gerarchia della vista per creare l'effetto sfocato che stiamo cercando. I componenti chiave sono:

  • Vista di sfondo: Questa vista mostra la foto e i crediti. Questa è la visione che stiamo per offuscare.
  • Immagine sfocata: Questa vista contiene una versione sfocata del contenuto della vista di sfondo.
  • Visualizza maschera: La maschera di visualizzazione è una vista opaca che useremo per mascherare l'immagine sfocata.
  • Scorri vista: La vista di scorrimento è la vista che contiene informazioni aggiuntive sulla foto. L'offset della vista di scorrimento viene utilizzato per aumentare o diminuire l'altezza della vista della maschera. Ciò comporterà l'immagine sfocata per rivelarsi in risposta allo scorrimento della visualizzazione di scorrimento.

risorse

Tutte le immagini, i framework e altri file necessari per questo tutorial sono inclusi nei file sorgente di questo tutorial. Clona il repository da GitHub o scarica i file sorgente per seguirlo.

2. Creazione dell'interfaccia utente

1. Panoramica

Aperto RootViewController.m in Xcode e dai un'occhiata al suo loadview metodo. È possibile ottenere una vista a volo d'uccello di come è strutturata l'interfaccia utente osservando l'implementazione di questo metodo. Ci sono tre principali sottoview nella nostra applicazione:

  • Vista dell'intestazione: Mostra il nome dell'applicazione
  • Visualizzazione del contenuto: Visualizza la foto e i crediti
  • Scorri vista: Include informazioni aggiuntive su una vista sfocata scorrevole

Invece di affollare il loadview metodo con l'inizializzazione e la configurazione delle sottoview, i metodi helper sono usati per eseguire il sollevamento pesante:

// content view [self.view addSubview: [self createContentView]]; // header view [self.view addSubview: [self createHeaderView]]; // scroll view [self.view addSubview: [self createScrollView]];

2. Creazione della vista dell'intestazione

La vista dell'intestazione contiene un rettangolo traslucido e un'etichetta di testo con il nome dell'applicazione.

- (UIView *) createHeaderView UIView * headerView = [[UIView alloc] initWithFrame: CGRectMake (0, 0, self.view.frame.size.width, 60)]; headerView.backgroundColor = [UIColor colorWithRed: 229 / 255,0 green: 39 / 255,0 blu: 34 / 255,0 alpha: 0,6]; UILabel * title = [[UILabel alloc] initWithFrame: CGRectMake (0, 20, self.view.frame.size.width, 40)]; title.text = @ "Dynamic Blur Demo"; ... [headerView addSubview: title]; return headerView; 

3. Creazione della vista del contenuto

La visualizzazione del contenuto mostra la foto e include anche i crediti fotografici in un cerchio bianco.

- (UIView *) createContentView UIView * contentView = [[UIView alloc] initWithFrame: self.view.frame]; // Immagine di sfondo UIImageView * contentImage = [[UIImageView alloc] initWithFrame: contentView.frame]; contentImage.image = [UIImage imageNamed: @ "demo-bg"]; [contentView addSubview: contentImage]; // Crediti fotografici UIView * creditsViewContainer = [[UIView alloc] initWithFrame: CGRectMake (self.view.frame.size.width / 2 - 65, 335, 130, 130)]; metaViewContainer.backgroundColor = [UIColor whiteColor]; metaViewContainer.layer.cornerRadius = 65; [contentView addSubview: creditsViewContainer]; UILabel * photoTitle = [[UILabel alloc] initWithFrame: CGRectMake (0, 54, 130, 18)]; photoTitle.text = @ "Peach Garden"; ... [metaViewContainer addSubview: photoTitle]; UILabel * photographer = [[UILabel alloc] initWithFrame: CGRectMake (0, 72, 130, 9)]; photographer.text = @ "di Cas Cornelissen"; ... [metaViewContainer addSubview: photographer]; return contentView; 

4. Creazione della vista di scorrimento

La vista di scorrimento contiene informazioni aggiuntive sulla foto e una versione sfocata della foto. La vista di scorrimento è circa il doppio della lunghezza dello schermo, con la metà inferiore contenente la visualizzazione del testo e la visualizzazione dell'immagine. Abilitando il paging sulla vista di scorrimento, i contenuti della vista di scorrimento si agganciano all'inizio o alla fine della vista, a seconda dell'offset della vista di scorrimento.

- (UIView *) createScrollView UIView * containerView = [[UIView alloc] initWithFrame: self.view.frame]; blurredBgImage = [[UIImageView alloc] initWithFrame: CGRectMake (0, 0, self.view.frame.size.width, 568)]; [blurredBgImage setContentMode: UIViewContentModeScaleToFill]; [containerView addSubview: blurredBgImage]; UIScrollView * scrollView = [[UIScrollView alloc] initWithFrame: self.view.frame]; scrollView.contentSize = CGSizeMake (self.view.frame.size.width, self.view.frame.size.height * 2 - 110); scrollView.pagingEnabled = YES; ... [containerView addSubview: scrollView]; UIView * slideContentView = [[Allocazione UIView] initWithFrame: CGRectMake (0, 518, self.view.frame.size.width, 508)]; slideContentView.backgroundColor = [UIColor clearColor]; [scrollView addSubview: slideContentView]; UILabel * slideUpLabel = [[UILabel alloc] initWithFrame: CGRectMake (0, 6, self.view.frame.size.width, 50)]; slideUpLabel.text = @ "Informazioni sulla foto"; ... [slideContentView addSubview: slideUpLabel]; UIImageView * slideUpImage = [[UIImageView alloc] initWithFrame: CGRectMake (self.view.frame.size.width / 2 - 12, 4, 24, 22.5)]; slideUpImage.image = [UIImage imageNamed: @ "up-arrow.png"]; [slideContentView addSubview: slideUpImage]; UITextView * detailsText = [[UITextView alloc] initWithFrame: CGRectMake (25, 100, 270, 350)]; detailsText.text = @ "Lorem ipsum ... laborum"; ... [slideContentView addSubview: detailsText]; restituire containerView; 

3. Scattare una foto

Per sfocare una vista, dobbiamo prima scattare un'istantanea dei suoi contenuti e averla pronta sotto forma di immagine. possiamo quindi sfocare l'immagine e utilizzarla come sfondo di un'altra vista. Vediamo innanzitutto come possiamo scattare un'istantanea dei contenuti di a UIView oggetto.

In iOS 7, Apple ha aggiunto un nuovo metodo a UIView per scattare istantanee dei contenuti di una vista, drawViewHierarchyInRect: afterScreenUpdates:. Questo metodo acquisisce un'istantanea dei contenuti della vista, comprese le eventuali sottoview che contiene.

Definiamo un metodo nel ViewController classe che accetta un UIView oggetto e restituisce a UIImage, un'istantanea dei contenuti della vista.

- (UIImage *) view TakeSnapshotOfView: (UIView *) UIGraphicsBeginImageContext (CGSizeMake (view.frame.size.width, view.frame.size.height)); [vista drawViewHierarchyInRect: CGRectMake (0, 0, view.frame.size.width, view.frame.size.height) afterScreenUpdates: YES]; UIImage * image = UIGraphicsGetImageFromCurrentImageContext (); UIGraphicsEndImageContext (); restituisci l'immagine; 

Passaggio 1: Inizia un nuovo contesto immagine

Un contesto immagine è un contesto grafico basato su bitmap che è possibile utilizzare per disegnare e manipolare le immagini. UIViewIl nuovo metodo drawViewHierarchyInRect: afterScreenUpdates: rasterizza il UIView e disegna i suoi contenuti nel contesto dell'immagine corrente.

Ciò significa che prima di chiamare questo metodo, dobbiamo prima creare un nuovo contesto di immagine invocando UIGraphicsBeginImageContext, passando la dimensione richiesta per il contesto dell'immagine.

Passaggio 2: Scatta un'istantanea

Con il contesto di immagine impostato, possiamo chiamare la vista drawViewHierarchyInRect: afterScreenUpdates: metodo. Il secondo argomento specifica se l'istantanea deve includere i contenuti correnti della vista o deve includere eventuali modifiche recenti prima di acquisire l'istantanea.

Passaggio 3: Crea immagine dal contesto dell'immagine

Possiamo ottenere il contenuto del contesto dell'immagine, l'istantanea della vista, invocando UIGraphicsGetImageFromCurrentImageContext. Questa funzione restituisce a UIImage oggetto.

Passaggio 4: fine contesto immagine

Dopo aver creato l'istantanea, rimuoviamo il contesto grafico dalla pila richiamando UIGraphicsEndImageContext.

4. Sfocare l'istantanea

Una volta ottenuta l'istantanea, possiamo sfocarla usando una serie di tecniche. In questo tutorial, tratterò tre tecniche:

  • sfocatura con il framework Core Image
  • sfocatura usando il framework GPUImage di Brad Larson
  • sfocatura usando Apple UIImage + ImageEffects categoria

Opzione 1: Immagine principale

Core Image è un framework di elaborazione delle immagini sviluppato e gestito da Apple. Utilizza un percorso di rendering GPU o CPU per elaborare le immagini quasi in tempo reale.

Core Image ci fornisce una varietà di filtri che possono essere utilizzati per eseguire operazioni che vanno dalla modifica di una tonalità o saturazione dell'immagine al rilevamento del volto.

CIGaussianBlur è uno dei filtri inclusi nel framework Core Image e può essere utilizzato per sfocare le immagini. Sfocare un'immagine con Core Image è abbastanza facile, come puoi vedere qui sotto.

- (UIImage *) blurWithCoreImage: (UIImage *) sourceImage CIImage * inputImage = [CIImage imageWithCGImage: sourceImage.CGImage]; // Applica il filtro Affine-Clamp per estendere l'immagine in modo che // non appaia ristretto quando viene applicata la sfocatura gaussiana CGAffineTransform transform = CGAffineTransformIdentity; CIFilter * clampFilter = [Filtro CIFilterWithName: @ "CIAffineClamp"]; [clampFilter setValue: inputImage forKey: @ "inputImage"]; [clampFilter setValue: [Valore NSValoreWithBytes: & transform objCType: @encode (CGAffineTransform)] forKey: @ "inputTransform"]; // Applica il filtro sfocatura gaussiana con raggio di 30 CIFilter * gaussianBlurFilter = [Filtro CIFilterWithName: @ "CIGaussianBlur"]; [gaussianBlurFilter setValue: clampFilter.outputImage forKey: @ "inputImage"]; [gaussianBlurFilter setValue: @ 30 forKey: @ "inputRadius"]; CIContext * context = [Contesto CIContextWithOptions: nil]; CGImageRef cgImage = [context createCGImage: gaussianBlurFilter.outputImage fromRect: [inputImage extent]]; // Imposta il contesto di output. UIGraphicsBeginImageContext (self.view.frame.size); CGContextRef outputContext = UIGraphicsGetCurrentContext (); // Inverte coordinate immagine CGContextScaleCTM (outputContext, 1.0, -1.0); CGContextTranslateCTM (outputContext, 0, -self.view.frame.size.height); // Disegna l'immagine di base. CGContextDrawImage (outputContext, self.view.frame, cgImage); // Applica la tinta bianca CGContextSaveGState (outputContext); CGContextSetFillColorWithColor (outputContext, [UIColor colorWithWhite: 1 alpha: 0.2] .CGColor); CGContextFillRect (outputContext, self.view.frame); CGContextRestoreGState (outputContext); // L'immagine di output è pronta. UIImage * outputImage = UIGraphicsGetImageFromCurrentImageContext (); UIGraphicsEndImageContext (); return outputImage; 

Rompiamo il codice sopra riportato:

  • Per prima cosa creiamo un CIImage oggetto dal UIImage oggetto. Quando si lavora con il framework Core Image, le immagini sono rappresentate da CIImage oggetti.
  • A seconda del raggio di sfocatura, l'applicazione di una sfocatura gaussiana a un'immagine riduce leggermente l'immagine. Per aggirare questo problema, allunghiamo un po 'l'immagine applicando un altro filtro, CIAffineClamp, prima di applicare il filtro sfocatura gaussiana.
  • Quindi prendiamo l'output e lo passiamo al CIGaussianBlur filtro con un raggio di sfocatura di 30.
  • Potremmo prendere l'output, convertirlo in a CGImage,  e usarlo nella nostra applicazione. Tuttavia, aggiungeremo una tinta bianca all'immagine per garantire che la descrizione della foto sia chiaramente leggibile. Per aggiungere una tinta bianca, aggiungiamo un riempimento bianco semitrasparente sull'immagine. Per fare ciò, creiamo un nuovo contesto immagine e lo riempiamo con un colore bianco con un valore alfa di 0.2.
  • Come abbiamo visto prima, otteniamo a UIImage oggetto dal contesto dell'immagine e smaltire il contesto dell'immagine.

Opzione 2: GPUImage

GPUImage è un framework iOS open source per l'elaborazione di immagini e video, creato e gestito da Brad Larson. Include una raccolta di filtri accelerati dalla GPU che possono essere applicati a immagini, video in diretta e filmati.

Il framework GPUImage è incluso nei file sorgente di questo tutorial, ma l'aggiunta del framework ai propri progetti è molto semplice:

  1. Inizia scaricando il framework o clonando il repository da GitHub.
  2. Aprire una finestra di terminale, accedere alla cartella GPUImage ed eseguire lo script di build build.sh compilare il quadro.
  3. Copia il GPUImage.framework dal costruire cartella nella cartella del progetto e quindi trascinarla nel Project Navigator.
  4. È quindi possibile utilizzare il framework GPUImage nel progetto importando le intestazioni framework, #importare .

Il framework GPUImage include filtri simili a quelli del framework Core Image. Per la nostra applicazione di esempio, siamo interessati a due filtri, , GPUImageGaussianBlurFilter e GPUImageiOSBlurFilter.

- (UIImage *) blurWithGPUImage: (UIImage *) sourceImage // Gaussian Blur GPUImageGaussianBlurFilter * blurFilter = [[GPUImageGaussianBlurFilter alloc] init]; blurFilter.blurRadiusInPixels = 30.0; return [blurFilter imageByFilteringImage: sourceImage]; 

Come puoi vedere, i filtri GPUImage sono più facili da usare rispetto a quelli del framework Core Image. Dopo aver inizializzato l'oggetto filtro, tutto ciò che devi fare è configurare il filtro e fornirlo con un'immagine a cui il filtro deve essere applicato. Il imageByFilteringImage: metodo restituisce a UIImage oggetto.

Invece del GPUImageGaussianblur classe, potresti anche usare il GPUImageiOSblur classe in questo modo:

 // iOS Blur GPUImageiOSBlurFilter * blurFilter = [[GPUImageiOSBlurFilter alloc] init]; blurFilter.blurRadiusInPixels = 30.0;

Il GPUImageiOSblur class replica l'effetto sfocato che puoi vedere in Control Center su iOS 7. Quindi, a differenza dell'approccio Core Image, non dovrai scrivere codice aggiuntivo per colorare l'immagine sfocata.

Opzione 3: UIImage + ImageEffects

Durante il WWDC dello scorso anno, Apple ha tenuto un discorso sugli effetti e le tecniche Core Image in cui ha introdotto una categoria UIImage chiamato ImageEffects. Il ImageEffects la categoria utilizza l'alta prestazione di Apple Vimage quadro di elaborazione delle immagini, parte del Accelerare quadro, per eseguire i calcoli necessari. Questo lo rende un modo semplice e veloce per eseguire la sfocatura su iOS.
La categoria aggiunge i seguenti metodi UIImage classe:

  • applyLightEffect
  • applyExtraLightEffect
  • applyDarkEffect
  • applyTintEffectWithColor:
  • applyBlurWithRadius: tintColor: saturationDeltaFactor: MaskImage:

Questi metodi possono essere chiamati direttamente su un'immagine per ottenere l'effetto sfocatura desiderato. Il applyBlurWithRadius: tintColor: saturationDeltaFactor: MaskImage: Il metodo accetta un numero di argomenti che consentono di ottimizzare l'operazione di sfocatura.

Puoi scaricare Apple ImageEffects esempio di progetto dal sito Web degli sviluppatori di Apple e utilizzalo nei tuoi progetti.

#import "UIImage + ImageEffects.h" ... - (UIImage *) blurWithImageEffects: (UIImage *) image return [image applyBlurWithRadius: 30 tintColor: [UIColor colorWithWhite: 1 alpha: 0.2] saturationDeltaFactor: 1.5 maskImage: nil]; 

5. Mascherare l'immagine sfocata

Nell'app di esempio, sembra che stiamo sfocando dinamicamente la fotografia, ma non è così. Usiamo un piccolo trucco chiamato mascheramento. Invece di scattare continuamente istantanee e sfocarle per creare l'effetto desiderato, scatta una sola istantanea, la sfocatura e la usiamo in combinazione con una maschera.

Come mostrato nella figura all'inizio di questo tutorial, allineiamo la vista sfocata con la vista di sfondo sottostante. Creiamo quindi un'altra vista con un'altezza di 50 punti e uno sfondo opaco e posizioniamola nella parte inferiore dello schermo. Usiamo questa vista per mascherare l'immagine sfocata.

blurredBgImage.layer.mask = bgMask.layer;

Quindi aggiorniamo la cornice della vista maschera mentre si scorre la vista a scorrimento. Per fare ciò, implementiamo uno dei metodi delegati di UIScrollViewDelegate protocollo, scrollViewDidScroll:, e aggiorna la cornice della maschera rispetto all'offset del contenuto verticale della vista a scorrimento.

-(void) scrollViewDidScroll: (UIScrollView *) scrollView bgMask.frame = CGRectMake (bgMask.frame.origin.x, self.view.frame.size.height - 50 - scrollView.contentOffset.y, bgMask.frame.size.width , bgMask.frame.size.height + scrollView.contentOffset.y); 

Aggiornando la maschera, appare come se stessimo dinamicamente sfocando la fotografia sotto la vista di scorrimento. Questo è tutto. Ora hai un bellissimo effetto sfocato, simile a quello che vedi nel Control Center su iOS.

6. Prestazioni

Con le tecniche di cui sopra in mente, potresti chiederti qual è il migliore in termini di prestazioni. Per aiutarti a decidere, ho eseguito alcuni test su un iPhone 5S e 5C. Dai un'occhiata ai seguenti grafici.

Questi grafici ci dicono quanto segue:

  • Il framework GPUImage è il più lento su iPhone 5C a causa della sua GPU più lenta. Questo non è sorprendente dal momento che il framework si basa molto sulla GPU.
  • La categoria ImageEffects si comporta meglio su entrambi i dispositivi. È anche interessante vedere che il tempo necessario per sfocare un'immagine aumenta con il raggio di sfocatura.

Mentre le immagini sfocate non richiedevano più di 220 ms su iPhone 5S, l'iPhone 5C aveva bisogno di un massimo di 1,3 secondi per eseguire lo stesso compito. Questo dimostra chiaramente che gli effetti di sfocatura dovrebbero essere usati con saggezza e scarsamente.

Per ridurre il tempo necessario per sfocare un'immagine, possiamo ridurre la dimensione dell'istantanea su cui applichiamo il filtro sfocatura. Poiché stiamo eseguendo una sfocatura e i dettagli più fini dell'immagine non saranno comunque visibili, possiamo ridimensionare l'immagine senza incorrere in problemi. Per fare uno snapshot più piccolo, aggiorniamo l'implementazione di takeSnapshotOfView: metodo come segue:

- (UIImage *) takeSnapshotOfView: (UIView *) view CGFloat reductionFactor = 1,25; UIGraphicsBeginImageContext (CGSizeMake (view.frame.size.width / reductionFactor, view.frame.size.height / reductionFactor)); [vista drawViewHierarchyInRect: CGRectMake (0, 0, view.frame.size.width / reductionFactor, view.frame.size.height / reductionFactor) afterScreenUpdates: YES]; ... return image; 

Per ridurre ulteriormente il tempo necessario per sfocare lo snapshot, possiamo usare tecniche di sfocatura alternativa, come la sfocatura di una finestra. Anche se il risultato sarà diverso da quello di una sfocatura gaussiana, ci vuole meno tempo per sfocare un'immagine usando una sfocatura a scatola.

Conclusione

Blur è sicuramente una grande aggiunta al design dell'interfaccia utente su iOS. Tuttavia, a prescindere dall'aspetto estetico dell'interfaccia utente dell'applicazione, se non funziona bene, gli effetti di sfocatura non migliorano l'applicazione.

Sulla base delle metriche prestazionali di cui sopra, vediamo che la sfocatura è davvero un effetto costoso. Ma ottimizzando i parametri, come il raggio di sfocatura e la dimensione dell'immagine, scegliendo la giusta tecnica di sfocatura e utilizzando alcuni trucchi, possiamo ottenere effetti molto interessanti senza compromettere le prestazioni dell'applicazione.

In iOS 8, Apple ha introdotto UIVisualEffectView, che consente agli sviluppatori di applicare molto facilmente gli effetti di sfocatura e vivacità alle viste. Se non puoi aspettare che venga rilasciato ufficialmente iOS 8, puoi provare questi effetti scaricando la versione beta di Xcode 6.