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.
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:
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.
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ă:
Î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]];
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;
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;
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;
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;
Un context de imagine este un context grafic bazat pe bitmap pe care îl puteți folosi pentru a desena și manipula imagini. UIView
noua 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.
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.
Putem obține conținutul contextului imaginii, instantaneu al vederii, invocând UIGraphicsGetImageFromCurrentImageContext
. Această funcție returnează a UIImage
obiect.
După crearea imaginii, eliminăm contextul grafic din stiva invocând UIGraphicsEndImageContext
.
Odată ce ați realizat instantaneu, îl putem blur folosind mai multe tehnici. În acest tutorial, voi acoperi trei tehnici:
UIImage + ImageEffects
categorieImaginea 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:
CIImage
obiect de la UIImage
obiect. Când lucrați cu cadrul Core Image, imaginile sunt reprezentate de CIImage
obiecte.CIAffineClamp
, înainte de aplicarea filtrului de estompare gaussian.CIGaussianBlur
filtru împreună cu o rază de estompare de 30
.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
.UIImage
obiecte din contextul imaginii și dispun de contextul imaginii.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ă:
#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ă.
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];
Î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.
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:
Î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.
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.