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.
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:
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.
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:
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]];
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;
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;
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;
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;
Un contesto immagine è un contesto grafico basato su bitmap che è possibile utilizzare per disegnare e manipolare le immagini. UIView
Il 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.
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.
Possiamo ottenere il contenuto del contesto dell'immagine, l'istantanea della vista, invocando UIGraphicsGetImageFromCurrentImageContext
. Questa funzione restituisce a UIImage
oggetto.
Dopo aver creato l'istantanea, rimuoviamo il contesto grafico dalla pila richiamando UIGraphicsEndImageContext
.
Una volta ottenuta l'istantanea, possiamo sfocarla usando una serie di tecniche. In questo tutorial, tratterò tre tecniche:
UIImage + ImageEffects
categoriaCore 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:
CIImage
oggetto dal UIImage
oggetto. Quando si lavora con il framework Core Image, le immagini sono rappresentate da CIImage
oggetti.CIAffineClamp
, prima di applicare il filtro sfocatura gaussiana.CIGaussianBlur
filtro con un raggio di sfocatura di 30
.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
.UIImage
oggetto dal contesto dell'immagine e smaltire il contesto dell'immagine.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:
#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.
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];
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.
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:
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.
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.