Adăugarea efectelor estompate pe iOS

Introducere

Cu iOS 7, am văzut o schimbare în paradigma de proiectare a Apple pentru dispozitivele mobile. Nu numai că au adoptat așa-numitul design plat, Apple a adăugat și ele câteva elemente ale acestui model. Una dintre aceste adăugiri este folosirea fundalurilor neclare translucide pentru a transmite noțiunea de profunzime și context. Lua Centrul de Control de exemplu, acesta estompează conținutul vederii din spatele acestuia, deoarece acesta este tras în sus. Aceasta conferă utilizatorului sentimentul că este poziționat de mai sus alt conținut de pe ecran și merită să se concentreze. Acest lucru se întâmplă fără ca utilizatorul să piardă unde este în aplicație.

Chiar dacă se utilizează blur și transluciditate pe tot sistemul de operare pe iOS 7, SDK-ul iOS nu ne oferă niciun API pentru a obține un efect similar. În acest tutorial, vă duc câteva metode pentru a crea vederi neclară, prin crearea aplicației de probă afișată mai jos.

Aplicația noastră de probă va avea o vedere în partea de jos, care poate fi dezvăluită prin tragere în sus. Vederea este translucidă și estompează conținutul care este sub el în ierarhia de vizualizare, similar cu Centrul de control pe iOS 7.

1. Configurarea proiectului

Prezentare generală

Aplicația pe care o vom construi va afișa o fotografie împreună cu numele și autorul fotografiei, afișate într-un cerc alb. În partea inferioară a ecranului va fi și o vedere dreptunghiulară mică, care va bloca fotografia și care poate fi trasă în sus pentru a afișa informații suplimentare despre fotografie.

Voi presupune că deja știți cum să lucrați cu elementele de bază ale interfeței de utilizator, cum ar fi vederi, vizualizări de imagini și vizualizări de defilare. În acest tutorial, ne vom concentra pe ierarhia de vizualizare și pe opiniile de care avem nevoie pentru a crea efectul de estompare.

Uitați-vă la imaginea de mai sus. Elimină ierarhia de vizualizare pentru a crea efectul de estompare pe care îl urmăm. Componentele cheie sunt:

  • Vizualizare fundal: Această vizualizare afișează fotografia și creditele. Aceasta este părerea pe care o vom înfunda.
  • Imagine neclară: Această vizualizare conține o versiune neclară a conținutului vizualizării de fundal.
  • Vizualizați masca: Masca de vizualizare este o vedere opacă pe care o vom folosi pentru a masca imaginea neclară.
  • Vizualizare derulare: Vizualizarea de defilare este afișarea care conține informații suplimentare despre fotografie. Poziția ecranului de derulare este utilizată pentru a mări sau a micșora înălțimea ecranului mască. Acest lucru va determina imaginea neclară să se dezvăluie ca răspuns la defilarea vederii scroll.

Resurse

Toate imaginile, cadrele și alte fișiere necesare acestui tutorial sunt incluse în fișierele sursă ale acestui tutorial. Clonați depozitul de la GitHub sau descărcați fișierele sursă de urmat.

2. Crearea interfeței utilizator

1. Prezentare generală

Deschis RootViewController.m în Xcode și aruncă o privire la ei loadView metodă. Puteți obține o vizualizare cu privire la modul în care este prezentată interfața cu utilizatorul, analizând implementarea acestei metode. Există trei subdiviziuni principale în cererea noastră:

  • Afișare antet: Afișează numele aplicației
  • Vizualizare conținut: Afișează fotografia și creditele
  • Derulați Vizualizare: Include informații suplimentare despre o vedere neclară scrollabilă

În loc să aglomerezi loadView metoda de inițializare și configurare a sub-viziunilor, metodele de ajutor sunt folosite pentru a face ridicarea greoaie:

// vizualizare conținut [self.view addSubview: [self createContentView]]; // vizualizare antet [auto.view addSubview: [self createHeaderView]]; // vizualizare parcurgere [auto.view addSubview: [self createScrollView]];

2. Crearea vederii

Vederea antet conține un dreptunghi translucid și o etichetă de text cu numele aplicației.

- (UIView *) createHeaderView UIView * headerView = [[UIView alocare] initWithFrame: CGRectMake (0, 0, self.view.frame.size.width, 60)]; headerView.backgroundColor = [Culoare UICcolorWithRed: 229 / 255.0 verde: 39 / 255.0 albastru: 34 / 255.0 alfa: 0.6]; UILabel * title = [[UILabel alocare] initWithFrame: CGRectMake (0, 20, auto.view.frame.size.width, 40)]; title.text = @ "Demo Blur dinamic"; ... [headerView addSubview: title]; retur headerView; 

3. Crearea vizualizării conținutului

Ecranul de conținut afișează fotografia și include, de asemenea, fotografiile dintr-un cerc alb.

- (UIView *) createContentView UIView * contentView = [[UIView alocare] initWithFrame: self.view.frame]; // Imagine de fundal UIImageView * contentImage = [[UIImageView aloca] initWithFrame: contentView.frame]; contentImage.image = [UIImage imageNamed: @ "demo-bg"]; [contentView addSubview: contentImage]; // Imagini de credit UIView * creditsViewContainer = [[UIView alocare] initWithFrame: CGRectMake (auto.view.frame.size.width / 2 - 65, 335, 130, 130)]; metaViewContainer.backgroundColor = [UICcolor whiteColor]; metaViewContainer.layer.cornerRadius = 65; [contentView addSubview: creditsViewContainer]; UILabel * photoTitle = [[UILabel alocare] initWithFrame: CGRectMake (0, 54, 130, 18)]; photoTitle.text = @ "Peach Garden"; ... [metaViewContainer addSubview: photoTitle]; UILabel * fotograf = [[UILabel alocare] initWithFrame: CGRectMake (0, 72, 130, 9)]; photographer.text = @ "de Cas Cornelissen"; ... [metaViewContainer addSubview: fotograf]; returnează contentView; 

4. Crearea vederii scroll

Ecranul de derulare conține informații suplimentare despre fotografie și o versiune neclară a fotografiei. Ecranul de derulare este de aproximativ două ori lungimea ecranului, jumătatea inferioară conținând afișarea textului și imaginea. Dacă activați paginarea în vizualizarea de derulare, conținutul ecranului de derulare se va apropia de partea superioară sau de jos a ecranului, în funcție de dispunerea ecranului de derulare.

- (UIView *) createScrollView UIView * containerView = [[UIView alocare] initWithFrame: self.view.frame]; blurredBgImage = [[UIImageView aloca] initWithFrame: CGRectMake (0, 0, self.view.frame.size.width, 568)]; [blurredBgImage setContentMode: UIViewContentModeScaleToFill]; [containerView addSubview: blurredBgImage]; UIScrollView * scrollView = [[Alocare UIScrollView] initWithFrame: self.view.frame]; scrollView.contentSize = CGSizeMake (auto.view.frame.size.width, self.view.frame.size.height * 2 - 110); scrollView.pentruEnabled = DA; ... [containerView addSubview: scrollView]; UIView * slideContentView = [[UIView alocare] initWithFrame: CGRectMake (0, 518, auto.view.frame.size.width, 508)]; slideContentView.backgroundColor = [UICcolor clearColor]; [scrollView addSubview: slideContentView]; UILabel * slideUpLabel = [[UILabel alocare] initWithFrame: CGRectMake (0, 6, self.view.frame.size.width, 50)]; slideUpLabel.text = @ "Informații despre fotografie"; ... [slideContentView addSubview: slideUpLabel]; UIImageView * slideUpImage = [[UIImageView alocare] initWithFrame: CGRectMake (auto.view.frame.size.width / 2 - 12, 4, 24, 22.5)]; slideUpImage.image = [UIImage imageNamed: @ "sus-arrow.png"]; [slideContentView addSubview: slideUpImage]; UITextView * detailsText = [[UITextView aliniere] initWithFrame: CGRectMake (25, 100, 270, 350)]; detailsText.text = @ "Lorem ipsum ... laborum"; ... [slideContentView addSubview: detailsText]; întoarcere containerView; 

3. Luarea unui instantaneu

Pentru a blur vizualizarea, trebuie mai întâi să luăm un instantaneu al conținutului său și să îl pregătim sub forma unei imagini. putem apoi blur imaginea și să o folosim ca fundal al unei alte viziuni. Să vedem mai întâi cum putem lua un instantaneu al conținutului unui UIView obiect.

În iOS 7, Apple a adăugat o nouă metodă pentru UIView pentru a obține instantanee ale conținutului unei imagini, drawViewHierarchyInRect: afterScreenUpdates:. Această metodă ia un instantaneu al conținutului vizualizării, inclusiv subiecții pe care îi conține.

Să definim o metodă în ViewController clasa care acceptă a UIView obiect și returnează a UIImage, un instantaneu al conținutului vizualizării.

- (UIImage *) takeSnapshotOfView: (UIView *) vedere UIGraphicsBeginImageContext (CGSizeMake (view.frame.size.width, view.frame.size.height)); [vizualizați drawViewHierarchyInRect: CGRectMake (0, 0, vizualizare.frame.size.width, view.frame.size.height) afterScreenUpdates: YES]; UIImage * imaginea = UIGraphicsGetImageFromCurrentImageContext (); UIGraphicsEndImageContext (); returnează imaginea; 

Pasul 1: Începeți un nou context de imagine

Un context de imagine este un context grafic bazat pe bitmap pe care îl puteți folosi pentru a desena și manipula imagini. UIViewnoua metodă drawViewHierarchyInRect: afterScreenUpdates: rasterizează UIView și atrage conținutul său în contextul actual al imaginii.

Aceasta înseamnă că înainte de a numi această metodă, trebuie mai întâi să creăm un nou context de imagine invocând UIGraphicsBeginImageContext, trecând la mărimea necesară pentru contextul imaginii.

Pasul 2: Faceți instantaneu

Cu contextul imagine setat, putem apela vizualizarea drawViewHierarchyInRect: afterScreenUpdates: metodă. Al doilea argument specifică dacă fotografia trebuie să includă conținutul curent al vizualizării sau trebuie să includă orice modificări recente înainte de a face instantaneu.

Pasul 3: Creați imagine din contextul imaginii

Putem obține conținutul contextului imaginii, instantaneu al vederii, invocând UIGraphicsGetImageFromCurrentImageContext. Această funcție returnează a UIImage obiect.

Pasul 4: Contextul final al imaginii

După crearea imaginii, eliminăm contextul grafic din stiva invocând UIGraphicsEndImageContext.

4. Blurirea instantaneului

Odată ce ați realizat instantaneu, îl putem blur folosind mai multe tehnici. În acest tutorial, voi acoperi trei tehnici:

  • estomparea cu cadrul Core Image
  • estomparea folosind cadrul GPUImage al lui Brad Larson
  • estompare folosind Apple's UIImage + ImageEffects categorie

Opțiunea 1: Imaginea de bază

Imaginea de bază este un cadru de procesare a imaginilor dezvoltat și întreținut de Apple. Utilizează o cale de redare GPU sau CPU pentru a procesa imagini în timp real în timp real.

Imaginea de bază ne furnizează o varietate de filtre care pot fi utilizate pentru a efectua operațiuni variind de la modificarea nuanței sau a saturației unei imagini în vederea detectării feței.

CIGaussianBlur este unul dintre filtrele incluse în cadrul Core Image și poate fi utilizat pentru a bloca imaginile. Blurring o imagine cu Core Imagine este destul de ușor, după cum puteți vedea mai jos.

- (UIImage *) blurWithCoreImage: (UIImage *) sursăImage CIImage * inputImage = [CIImage imageWithCGImage: sourceImage.CGImage]; // Aplicați filtrul Affine-Clamp pentru a întinde imaginea astfel încât să nu / / arăta scrântit atunci când este aplicată blurul gaussian CGAffineTransform transform = CGAffineTransformIdentity; CIFilter * clampFilter = [CIFilter filterWithName: @ "CIAffineClamp"]; [clampFilter setValue: inputImage pentruKey: @ "inputImage"]; [clampFilter setValue: [Valoare NSValueWithBytes: & transformare objCType: @encode (CGAffineTransform)] pentruKey: @ "inputTransform"]; // Aplicați filtrul de estompare gaussian cu raza de 30 CIFilter * gaussianBlurFilter = [CIFilter filterWithName: @ "CIGaussianBlur"]; [gaussianBlurFilter setValue: clampFilter.outputImage pentruKey: @ "inputImage"]; [gaussianBlurFilter setValue: @ 30 pentruKey: @ "inputRadius"]; CIContext * context = [context CIContextWithOptions: nil]; CGImageRef cgImage = [context creaCGImage: gaussianBlurFilter.outputImage dinRect: [inputImage extent]]; // Configurați contextul de ieșire. UIGraphicsBeginImageContext (self.view.frame.size); CGContextRef outputContext = UIGraphicsGetCurrentContext (); // Inversați coordonatele imaginii CGContextScaleCTM (outputContext, 1.0, -1.0); CGContextTranslateCTM (outputContext, 0, -self.view.frame.size.height); // Desenați imaginea de bază. CGContextDrawImage (outputContext, self.view.frame, cgImage); // Aplicați culoarea albă CGContextSaveGState (outputContext); CGContextSetFillColorWithColor (outputContext, [UICcolor colorWithWhite: 1 alfa: 0.2] .CGColor); CGContextFillRect (outputContext, self.view.frame); CGContextRestoreGState (outputContext); // Imaginea de ieșire este gata. UIImage * outputImage = UIGraphicsGetImageFromCurrentImageContext (); UIGraphicsEndImageContext (); retur outputImage; 

Să rupem blocul de mai sus:

  • Mai întâi creăm un CIImage obiect de la UIImage obiect. Când lucrați cu cadrul Core Image, imaginile sunt reprezentate de CIImage obiecte.
  • În funcție de raza de estompare, aplicarea unei estompări gaussian la o imagine va micșora ușor imaginea. Pentru a rezolva această problemă, întindem imaginea puțin, aplicând un alt filtru, CIAffineClamp, înainte de aplicarea filtrului de estompare gaussian.
  • Apoi luăm rezultatul și îl transmitem către CIGaussianBlur filtru împreună cu o rază de estompare de 30.
  • Am putea lua ieșirea, convertiți-o la a CGImage,  și folosiți-o în aplicația noastră. Cu toate acestea, vom adăuga o nuanță albă imaginii pentru a vă asigura că descrierea fotografiei este clar lizibilă. Pentru a adăuga o nuanță albă, adăugăm o imagine albă semi-transparentă peste imagine. Pentru a face acest lucru, creăm un nou context de imagine și îl umplem cu o culoare albă, cu o valoare alfa de 0.2.
  • După cum am văzut mai devreme, vom obține a UIImage obiecte din contextul imaginii și dispun de contextul imaginii.

Opțiunea 2: GPUImage

GPUImage este un cadru open source pentru iOS pentru procesarea imaginilor și a imaginilor, creat și întreținut de Brad Larson. Acesta include o colecție de filtre accelerate de GPU care pot fi aplicate imaginilor, filmelor video live și filmelor.

Cadrul GPUImage este inclus în fișierele sursă ale acestui tutorial, însă adăugarea cadrului pentru proiectele proprii este foarte ușoară:

  1. Începeți prin descărcarea cadrului sau clonarea depozitului de la GitHub.
  2. Deschideți o fereastră de terminal, navigați la dosarul GPUImage și executați scriptul de construire build.sh pentru a compila cadrul.
  3. Copiați GPUImage.framework de la construi folder în dosarul proiectului și apoi trageți-l și plasați-l în Project Navigator.
  4. Apoi, puteți utiliza cadrul GPUImage în proiectul dvs. importând anteturile de cadre, #import .

Cadrul GPUImage include filtre similare celor din cadrul Core Image. Pentru exemplul nostru de aplicare, suntem interesați de două filtre, , GPUImageGaussianBlurFilter și GPUImageiOSBlurFilter.

- (UIImage *) blurWithGPUImage: (UIImage *) sursăImage // Gaussian Blur GPUImageGaussianBlurFilter * blurFilter = [[GPUImageGaussianBlurFilter alocare] init]; blurFilter.blurRadiusInPixels = 30.0; retur [blurFilter imageByFilteringImage: sourceImage]; 

După cum puteți vedea, filtrele GPUImage sunt mai ușor de folosit decât cele ale cadrului Core Image. După inițializarea obiectului de filtrare, tot ce trebuie să faceți este să configurați filtrul și să îl furnizați cu o imagine la care trebuie aplicat filtrul. imageByFilteringImage: metoda returnează a UIImage obiect.

In loc de GPUImageGaussianblur clasa, puteți utiliza și GPUImageiOSblur clasa ca atare:

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

GPUImageiOSblur clasa replică efectul de estompare pe care îl puteți vedea în Control Center pe iOS 7. Deci, spre deosebire de abordarea Core Image, nu va trebui să scrieți cod suplimentar pentru a nuanța imaginea neclară.

Opțiunea 3: UIImage + ImageEffects

În timpul anului WWDC de anul trecut, Apple a discutat despre efectele și tehnicile Core Image în care au introdus o categorie UIImage denumit ImageEffects. ImageEffects categoria utilizează performanțele ridicate ale Apple vImage cadru de procesare a imaginii, o parte din Accelera cadru, pentru a efectua calculele necesare. Aceasta face o modalitate rapidă și ușoară de a efectua blur în iOS.
Categoria adaugă următoarele metode UIImage clasă:

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

Aceste metode pot fi apelate direct pe o imagine pentru a obține efectul de estompare dorit. applyBlurWithRadius: tintColor: saturationDeltaFactor: maskImage: Metoda acceptă un număr de argumente care vă permit să finalizați operația de estompare.

Puteți descărca Apple's ImageEffects proiect de proba de pe site-ul dezvoltatorului Apple și să-l utilizați în proiectele dvs..

#import "UIImage + ImageEffects.h" ... - (UIImage *) blurWithImageEffects: (UIImage *) imagine retur [image applyBlurWithRadius: 30 tintColor: [ali: 0.2 saturationDeltaFactor: 1.5 maskImage: nil]; 

5. Mascarea imaginii neclare

În aplicația pentru eșantion, apare ca și cum vom bloca dinamic fotografia, dar acest lucru nu este cazul. Folosim un mic truc numit mascare. În loc să luăm instantaneu instantanee și să le estompem pentru a crea efectul dorit, luăm doar o singură imagine, o estompează și o folosim în combinație cu o mască.

După cum se arată în figura de la începutul acestui tutorial, aliniem vizualizarea neclară cu afișarea de fundal de sub ea. Apoi vom crea o altă imagine cu o înălțime de 50 de puncte și un fond opac și o vom plasa în partea de jos a ecranului. Utilizăm această vizualizare pentru a masca imaginea neclară.

blurredBgImage.layer.mask = bgMask.layer;

Apoi, actualizăm cadrul afișării mască pe măsură ce derulați ecranul de defilare. Pentru a face acest lucru, implementăm una din metodele delegate ale UIScrollViewDelegate protocol, scrollViewDidScroll:, și actualizați cadrul mascului în raport cu decalajul conținutului vertical al vizualizării de derulare.

-(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); 

Actualizând masca, apare ca și cum vom bloca dinamic fotografia de sub afișarea de defilare. Asta e. Acum aveți un efect frumos, neclar, similar celui pe care îl vedeți în Centrul de control de pe iOS.

6. Performanță

Având în vedere tehnicile de mai sus în minte, s-ar putea să vă întrebați care dintre ele este cel mai bun în termeni de performanță. Pentru a vă ajuta să decideți, am efectuat câteva teste pe un iPhone 5S și 5C. Uitați-vă la următoarele grafice.

Aceste grafice ne spun următoarele:

  • Cadrul GPUImage este cel mai lent pe iPhone 5C datorită GPU-ului său mai lent. Acest lucru nu este surprinzător, deoarece cadrul se bazează foarte mult pe unitatea de procesare grafică.
  • Categoria ImageEffects se comportă cel mai bine pe ambele dispozitive. De asemenea, este interesant să vedem că timpul necesar pentru estomparea unei imagini crește cu raza de neclaritate.

În timp ce blurrea imaginilor nu a durat niciodată mai mult de 220ms pe iPhone 5S, iPhone 5C avea nevoie de până la 1,3s pentru a efectua aceeași sarcină. Acest lucru arată în mod clar că efectele de estompare ar trebui să fie utilizate cu înțelepciune și în mod redus.

Pentru a reduce timpul necesar pentru estomparea unei imagini, putem reduce dimensiunea imaginii pe care o aplicăm filtrul neclar. Din moment ce realizăm o estompare și detaliile mai fine ale imaginii nu vor fi vizibile oricum, putem schimba imaginea fără a face probleme. Pentru a face un instantaneu mai mic, actualizăm implementarea takeSnapshotOfView: după cum urmează:

- (UIImage *) takeSnapshotOfView: (UIView *) vizualizare CGFloat reductionFactor = 1.25; UIGraphicsBeginImageContext (CGSizeMake (view.frame.size.width / reduceFactor, view.frame.size.height / reductionFactor)); [vizualizați drawViewHierarchyInRect: CGRectMake (0, 0, view.frame.size.width / reductionFactor, view.frame.size.height / reductionFactor) afterScreenUpdates: YES]; ... return image; 

Pentru a reduce în continuare timpul necesar pentru a blur imaginea, putem folosi tehnici alternative de estompare, cum ar fi o blur de box. Chiar dacă rezultatul va fi diferit de cel al unei estompare gaussiană, este nevoie de mai puțin timp pentru a blur imaginea folosind o blur de box.

Concluzie

Blur este cu siguranta un plus extraordinar pentru designul interfetei utilizator pe iOS. Cu toate acestea, indiferent de cât de minunat arată interfața de utilizare a aplicației dvs., dacă nu funcționează bine, efectele estompate nu vor îmbunătăți aplicația.

Pe baza măsurătorilor de performanță de mai sus, vedem că estomparea este într-adevăr un efect costisitor. Dar, prin optimizarea parametrilor, cum ar fi raza de neclaritate și dimensiunea imaginii, alegerea tehnicii de estompare corectă și folosirea câtorva trucuri, putem obține efecte foarte interesante fără a compromite performanța aplicației.

În iOS 8, Apple a introdus UIVisualEffectView, care permite dezvoltatorilor să aplice cu ușurință efecte de estompare și vibrații asupra vederilor. Dacă nu puteți aștepta până când versiunea iOS 8 este lansată oficial, puteți testa aceste efecte descărcând beta-ul Xcode 6.

Cod