自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

iOS 7中實(shí)現(xiàn)模糊效果教程

移動(dòng)開發(fā) iOS
iOS 7在視覺方面有許多改變,其中非常吸引人的功能之一就是在整個(gè)系統(tǒng)中巧妙的使用了模糊效果。許多第三方應(yīng)用程序已經(jīng)采用了這樣的設(shè)計(jì)細(xì)節(jié),并以各種奇妙的和具有創(chuàng)造性的方式使用它。本文將通過幾種不同的技術(shù)來實(shí)現(xiàn)iOS 7中的模糊效果。

本文譯自iOS 7 Blur Effects with GPUImage。

iOS 7在視覺方面有許多改變,其中非常吸引人的功能之一就是在整個(gè)系統(tǒng)中巧妙的使用了模糊效果。許多第三方應(yīng)用程序已經(jīng)采用了這樣的設(shè)計(jì)細(xì)節(jié),并以各種奇妙的和具有創(chuàng)造性的方式使用它。

本文將通過幾種不同的技術(shù)來實(shí)現(xiàn)iOS 7中的模糊效果,當(dāng)然,這一切都利用了一個(gè)名為GPUImage的框架。

GPUImage是由Brad Larson創(chuàng)建的,它利用GPU,使在圖片和視頻上應(yīng)用不同的效果和濾鏡變得非常的容易,同時(shí)它還擁有出色的性能,并且它的性能要比蘋果內(nèi)置的相關(guān)APIs出色。

注意:本文需要一臺(tái)物理設(shè)備來編譯并運(yùn)行示例程序(在模擬器上無法使用)。同樣還需要一個(gè)iOS開發(fā)者賬號(hào)。如果你還沒有開發(fā)者賬號(hào)的 話,可以來[這里](https://developer.apple.com/)注冊(cè)一個(gè)。注冊(cè)為開發(fā)者之后,會(huì)有許多福利喲,例如可以使用物理設(shè)備來 開發(fā)程序,提前獲得蘋果的相關(guān)測(cè)試版程序,以及大量的開發(fā)資源。

iOS中利用GPUImage實(shí)現(xiàn)模糊效果

iOS中利用GPUImage實(shí)現(xiàn)模糊效果

下面我們先來看看本文的目錄結(jié)構(gòu):

  • 開始
  • 為什么要是用模糊效果
    • 深度引導(dǎo)
    • 上下文
    • 關(guān)注度
  • 添加靜態(tài)的模糊效果
    • 創(chuàng)建截圖Category
    • 利用斷點(diǎn)測(cè)試截屏圖片
    • 顯示截屏圖片
    • 設(shè)置contentsRect
    • 重置模糊濾鏡
    • 對(duì)其背景圖片
  • 實(shí)時(shí)模糊
  • 線程中簡潔的分支
  • 一些潛在的實(shí)時(shí)模糊方案
  • 一個(gè)折中的方法——對(duì)視頻實(shí)時(shí)模糊
    • 利用GPUImage對(duì)視頻進(jìn)行模糊處理
  • 何去何從?

開始

首先先來這里下載本文的starter工程,并將其解壓出來。

用Xcode打開Video Blurring.xcodeproj,并將工程運(yùn)行到設(shè)備中。此時(shí)看到程序的效果如下所示:

[[110923]]

點(diǎn)擊屏幕左上角的菜單(三條橫紋),可以看到界面中出現(xiàn)兩個(gè)選項(xiàng):錄制視頻和播放已有視頻。

請(qǐng)注意,現(xiàn)在所有的用戶界面都有一個(gè)灰色的背景,是不是感覺有點(diǎn)沉悶?zāi)?,本文我們就利用iOS 7中的模糊效果來替換掉這些沉悶的灰色背景。

為什么要是用模糊效果

除了外觀看起來很棒以外,模糊效果還可以讓程序給用戶帶來3個(gè)重要的概念:深度引導(dǎo)、上下文和關(guān)注度。

深度引導(dǎo)

在用戶界面上,模糊效果可以給用戶提供一個(gè)深度引導(dǎo)效果,并且有利于用戶對(duì)程序?qū)Ш降睦斫?。在之前的iOS版本中的深度引導(dǎo)效果是通過:三維斜面 (three-dimensional bevels)和有關(guān)澤的按鈕(反映出一個(gè)模擬的光源),而在iOS 7中是通過模糊和視差(parallax)來實(shí)現(xiàn)的。

這里說的視差效果,可以很明顯的觀察出來:在裝有iOS 7的設(shè)備中,將設(shè)備從一側(cè)傾斜至另一側(cè),會(huì)發(fā)現(xiàn)設(shè)備中的圖標(biāo)在移動(dòng)(會(huì)獨(dú)立于背景)。這樣可以給用戶做出一個(gè)提示:界面是由不同的層構(gòu)成的,并且重要的界 面元素是在最前面的——這也涉及到下面將要介紹的一個(gè)概念:上下文。

上下文

上下文可以讓用戶在程序內(nèi)獲得一種軸承的感覺。動(dòng)畫的過度效果就提供了一種非常優(yōu)秀的上下文,當(dāng)用戶點(diǎn)擊一個(gè)按鈕時(shí),在兩個(gè)view之間利用動(dòng)畫效 果來切換畫面(而不是直接顯示一個(gè)新的view),可以讓用戶知道新的view是從哪里出現(xiàn)的,并且可以讓用戶很容易知道如何回到上一個(gè)view。

模糊效果可以將上一個(gè)view當(dāng)做背景顯示出來,盡管上一個(gè)view已經(jīng)失去焦點(diǎn)了,不過可以給用戶提供更多的上下文:剛剛是在哪里。通知中心就是一個(gè)非常棒的例子:當(dāng)拉下通知中心時(shí),我們可以在背景中看到原來的view(即使現(xiàn)在正在處于通知中心界面)。

關(guān)注度

讓界面更加關(guān)注于某些選擇項(xiàng)上,而移除不需要的內(nèi)容,讓用戶可以更加快捷的進(jìn)行導(dǎo)航。用戶可以本能的忽略那些被模糊的界面元素,而將注意力集中到某些界面元素中。

通過本文,你將學(xué)到兩種模糊類型的實(shí)現(xiàn)方法:靜態(tài)模糊和動(dòng)態(tài)模糊。靜態(tài)模糊代表著快照的時(shí)間點(diǎn),它并不能反映被模糊界面元素的變化。大多數(shù)情況下,使用靜態(tài)模糊效果就足夠了。相反,動(dòng)態(tài)模糊則是對(duì)需要模糊的背景做出實(shí)時(shí)更新。

相信看到具體的效果才是最好的,下面我們就來看看模糊效果的具體實(shí)現(xiàn)吧!

添加靜態(tài)的模糊效果

創(chuàng)建一個(gè)靜態(tài)模糊效果首先是將當(dāng)前屏幕中的view轉(zhuǎn)換為一幅圖片。獲得圖片之后,只需要對(duì)圖片做模糊處理就可以了。將view轉(zhuǎn)換為一幅圖片(截屏)蘋果已經(jīng)提供了一些非常棒的APIs了,并且在iOS 7中又有了新的方法可以讓截屏更加快速。

這些新的方法屬于截屏APIs中的一部分,截屏APIs不僅可以對(duì)某個(gè)view截屏,還能把整個(gè)view層次截屏,如果你希望對(duì)某個(gè)view截屏,那么可以把view中的按鈕、標(biāo)簽、開關(guān)等各種view也進(jìn)行截屏。

此處我們將截屏的邏輯實(shí)現(xiàn)到UIView的一個(gè)category中。這樣一來,我們就可以很方便快捷的將任意的view(以及view中的內(nèi)容)轉(zhuǎn)換為一個(gè)圖片——也算是代碼的重用吧。

創(chuàng)建截圖Category

打開File/New/File...,然后選擇iOS/Cocoa Touch/Objective-C category,如下圖所示:

將這個(gè)category命名為Screenshot,并將它的category選為UIView,如下圖所示:

將下面這個(gè)方法聲明到UIView+Screenshot.h中:

  1. -(UIImage *)convertViewToImage; 

接著將如下方法添加到 UIView+Screenshot.m 中:

  1. -(UIImage *)convertViewToImage 
  2.     UIGraphicsBeginImageContext(self.bounds.size); 
  3.     [self drawViewHierarchyInRect:self.bounds afterScreenUpdates:YES]; 
  4.     UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); 
  5.     UIGraphicsEndImageContext(); 
  6.  
  7.     return image; 

上面的方法中,首先調(diào)用了UIGraphicsBeginImageContext(),最后調(diào)用的是UIGraphicsEndImageContext(),這兩行代碼可以理解為圖形上下文的一個(gè)事物處理過程。一個(gè)上下文可以理解為不同的概念,例如屏幕,或者此處可以理解為一幅圖片。這里的兩行代碼起到一個(gè)離屏畫布的作用——可以將view繪制上去。

drawViewHierarchyInRect:afterScreenUpdates:方法利用view層次結(jié)構(gòu)并將其繪制到當(dāng)前的上下文中。

最后,UIGraphicsGetImageFromCurrentImageContext()從圖形上下文中獲取剛剛生成的UIImage。

現(xiàn)在,我們已經(jīng)完成了category的實(shí)現(xiàn),接著我們只需要在使用到的地方將其import一下即可。

如下代碼所示,將代碼添加到DropDownMenuController.m頂部:

  1. #import "UIView+Screenshot.h" 

同時(shí),將如下方法添加到相同的文件中:

  1. -(void)updateBlur 
  2.     UIImage *image = [self.view.superview convertViewToImage]; 

上面的代碼確保是對(duì)superview進(jìn)行截屏,而不僅僅是當(dāng)前的view。不這樣做的話,截屏獲得的圖片只是menu本身。

利用斷點(diǎn)測(cè)試截屏圖片

為了測(cè)試截屏的效果,我們?cè)赾onvertViewToImage調(diào)用的下面一行添加一個(gè)斷點(diǎn)。這樣當(dāng)命中斷點(diǎn)時(shí),程序會(huì)在斷點(diǎn)中暫停執(zhí)行,這樣我們就可以看到截屏的圖片,以此確保截屏代碼的正確性:

在測(cè)試之前還有一件事情需要做:調(diào)用上面這個(gè)方法。

找到show方法,并在addToParentViewController下面直接調(diào)用一下updateBlur:

  1. -(void)show { 
  2.     [self addToParentViewController]; 
  3.  
  4.     [self updateBlur]; // Add this line 
  5.  
  6.     CGRect deviceSize = [UIScreen mainScreen].bounds; 
  7.  
  8.     [UIView animateWithDuration:0.25f animations:^(void){ 
  9.         _blurView.frame = CGRectMake(0, 0, deviceSize.size.height, MENUSIZE); 
  10.         _backgroundView.frame = CGRectMake(0, 0, _backgroundView.frame.size.width, MENUSIZE); 
  11.     }]; 

編譯并運(yùn)行程序,點(diǎn)擊菜單按鈕,可以看到Xcode在斷點(diǎn)出停止了,如下所示:

在debugger左下角hand pane中選擇image,然后單擊快速查找圖標(biāo)按鈕,就可以預(yù)覽剛剛的截屏啦:

如上圖所示,正是我們所預(yù)期的。

顯示截屏圖片

將截取到的圖片顯示到菜單的背景中就是小菜一碟啦。

一般來說我們都會(huì)利用UIImageView來顯示一幅圖片,而由于我們要利用GPUImage來模糊圖片,所以需要使用GPUImageView。

在這里的工程中,已經(jīng)添加好了GPUImage框架,我們只需要將頭文件import一下即可。

將下面的代碼添加到DropDownMenuController.m頂部:

  1. #import <GPUImage/GPUImage.h> 

注意:GPUImage被包含在一個(gè)框架中,所以在import語句中,需要利用尖括弧,而不是雙引號(hào)。

此時(shí),有一個(gè)_blurView,類型為UIView——是菜單的灰色背景。將UIView修改為GPUImageView,如下所示:

  1. @implementation DropDownMenuController { 
  2.     GPUImageView *_blurView; 
  3.     UIView *_backgroundView; 

修改之后,Xcode會(huì)報(bào)一個(gè)warning:大意是你利用UIView進(jìn)行實(shí)例化,而不是預(yù)期的GPUImageView。

可以通過下面的方法消除這個(gè)警告,在viewDidLad中修改做如下修改:

  1. _blurView = [[GPUImageView alloc] initWithFrame:CGRectMake(0, 0, deviceSize.size.height, 0)]; 

緊隨其后,將如下兩行代碼添加進(jìn)去,并移除設(shè)置背景色的代碼:

  1. _blurView.clipsToBounds = YES; 
  2. _blurView.layer.contentsGravity = kCAGravityTop; 

clipToBounds屬性設(shè)置為YES,把超出_blurView范圍的子view隱藏起來,而contentsGravity確保圖片出現(xiàn)在image view的頂部。

由于_blurView已經(jīng)用于背景了,所以此處不需要額外設(shè)置了。

接著,我們還需要聲明一個(gè)用于模糊效果的過濾器。

將如下代碼添加到DropDownMenuController.m:文件的@implementation中:

  1. GPUImageiOSBlurFilter *_blurFilter; 

找到之前添加的斷點(diǎn),右鍵單擊,并選中Delete Breakpoint:

下面是非常重要的一步了——初始化模糊濾鏡。將如下代碼添加到DropDownMenuController.m中:

  1. -(void)updateBlur 
  2.     if(_blurFilter == nil){ 
  3.         _blurFilter = [[GPUImageiOSBlurFilter alloc] init]; 
  4.          _blurFilter.blurRadiusInPixels = 1.0f; 
  5.  
  6.     } 
  7.  
  8.     UIImage *image = [self.view.superview convertViewToImage]; 

注意:上面將模糊半徑設(shè)置為一個(gè)像素,這里暫時(shí)將這個(gè)值設(shè)置低一點(diǎn),這樣可以確保圖片的正確定位,當(dāng)一切ok之后,再增加模糊半徑即可。

下面是時(shí)候?qū)D片顯示到GPUImageView中了。不過并不是簡單的實(shí)例化一個(gè)UIImage,并將其添加到GPUImageView中。首先需創(chuàng)建一個(gè)GPUImagePicture。

將如下代碼添加到updateBlur方法的底部:

  1. GPUImagePicture *picture = [[GPUImagePicture alloc] initWithImage:image]; 

至此,我們獲得了一個(gè)圖片,模糊濾鏡和iamge view。

接著再將如下代碼添加到updateBlur底部:

  1. [picture addTarget:_blurFilter]; 
  2. [_blurFilter addTarget:_blurView]; 
  3.  
  4. [picture processImage]; 

上面這幾行代碼,就像膠水一樣,將所有的事情關(guān)聯(lián)起來。將濾鏡當(dāng)做target添加到圖片中,然后將image view當(dāng)做濾鏡的target。

上面代碼對(duì)圖片的處理全程發(fā)生在GPU上,也就是說當(dāng)進(jìn)行模糊計(jì)算和顯示時(shí),并不會(huì)影響到用戶界面。當(dāng)處理結(jié)束時(shí),會(huì)把圖片顯示到image view上面。

編譯并運(yùn)行程序,點(diǎn)擊菜單按鈕,可以看到如下類似畫面:

上面的圖片看起來是不是有點(diǎn)奇怪?看到的圖片被縮放到適配到菜單視圖中了。要對(duì)此做出修正,我們需要指定圖片的哪一部分需要顯示在GPUImageView中——也就是處理截屏視圖的上半部分。

設(shè)置contentsRect

按照如下代碼所示修改DropDownMenuController.m文件中的show方法:

  1. -(void)show 
  2.     [self addToParentViewController]; 
  3.  
  4.     [self updateBlur]; 
  5.  
  6.     CGRect deviceSize = [UIScreen mainScreen].bounds; 
  7.  
  8.     [UIView animateWithDuration:0.25f animations:^(void){ 
  9.         _blurView.frame = CGRectMake(0.0f, 0.0f, deviceSize.size.height, MENUSIZE); 
  10.         _backgroundView.frame = CGRectMake(0.0f, 0.0f, _backgroundView.frame.size.width, MENUSIZE); 
  11.         _blurView.layer.contentsRect = CGRectMake(0.0f, 0.0f, 1.0f, MENUSIZE / 320.0f); // Add this line! 
  12.     }]; 

通過指定_blurView.layer.contentsRect來定義一個(gè)矩形,在單元坐標(biāo)空間(unit coordinate space)中,表示只使用layer content的一部分。

編譯并運(yùn)行程序,點(diǎn)擊菜單按鈕,會(huì)看到如下圖所示效果:

雖然已經(jīng)使用了圖片的一部分,看起來還是不正確,這是因?yàn)樗目s放比例還不適合!此處還缺少對(duì)正確內(nèi)容的縮放。

將下面這行代碼添加到show方法中動(dòng)畫block的尾部:

  1. _blurView.layer.contentsScale = (MENUSIZE / 320.0f) * 2; 

contentsScale屬性聲明了layer在邏輯坐標(biāo)空間(以點(diǎn)為單位)和物理坐標(biāo)空間(以像素為單位)之間的映射關(guān)系。更高比例因子表示在渲染layer時(shí),一個(gè)點(diǎn)代表著多個(gè)像素點(diǎn)。

編譯并運(yùn)行程序,點(diǎn)擊菜單按鈕,可以看到縮放比例已經(jīng)正常了:

沒錯(cuò)——看起來好多了!現(xiàn)在關(guān)閉程序,然后重新打開,ou~~發(fā)生了什么?如下圖所示:

看起來這還是有點(diǎn)問題。如果在對(duì)view進(jìn)行animation之前將contentScale設(shè)置回2.0,會(huì)解決half bar的問題。

將如下代碼添加到DropDownMenuController.m中show方法里面的animation block上面:

  1. _blurView.layer.contentsScale = 2.0f; 

編譯并運(yùn)行程序,然后點(diǎn)擊菜單,接著關(guān)閉菜單,再打開菜單,此時(shí)菜單開起來如下所示:

現(xiàn)在半個(gè)尺寸的黑色box已經(jīng)沒有問題了——但是現(xiàn)在是全尺寸的黑色box!

重置模糊濾鏡

上面問題產(chǎn)生的原因是由于進(jìn)行了二次模糊計(jì)算。解決的方法是移除模糊濾鏡中的所有target。如果不這樣做的話,之后對(duì)濾鏡的調(diào)用不會(huì)輸出任何的內(nèi)容——進(jìn)而引起黑色box的問題。

按照如下代碼更新一下updateBlur方法:

  1. -(void)updateBlur 
  2.     if(_blurFilter == nil){ 
  3.         _blurFilter = [[GPUImageiOSBlurFilter alloc] init]; 
  4.         _blurFilter.blurRadiusInPixels = 1.0f; 
  5.     } 
  6.  
  7.     UIImage *image = [self.view.superview convertViewToImage]; 
  8.  
  9.     GPUImagePicture *picture = [[GPUImagePicture alloc] initWithImage:image]; 
  10.     [picture addTarget:_blurFilter]; 
  11.     [_blurFilter addTarget:_blurView]; 
  12.  
  13.     [picture processImageWithCompletionHandler:^{ 
  14.         [_blurFilter removeAllTargets]; 
  15.     }]; 

上面的代碼用processImageWithCompletionHandler:替換了processImage方法。這個(gè)新的方法有一個(gè)completion block,當(dāng)image 處理結(jié)束時(shí),會(huì)運(yùn)行這個(gè)block。一旦image處理結(jié)束,我們就可以安全的將濾鏡中的target全部移除。

編譯并運(yùn)行程序,點(diǎn)擊菜單,檢查一下黑色box問題是不是已經(jīng)解決掉了:

多次打開和關(guān)閉菜單,確保之前的那個(gè)bug已經(jīng)解決掉啦!

現(xiàn)在仔細(xì)觀察一下打開菜單的模糊效果——有些東西看起來不正確。為了更加明顯的觀察到問題,我們減慢動(dòng)畫的時(shí)間,讓其慢慢的移動(dòng)。

在show方法中,將animation bloc的持續(xù)時(shí)間修改為10.0f。

編譯并運(yùn)行程序,點(diǎn)擊菜單,然后觀察一下菜單出場(chǎng)的慢動(dòng)作:

恩,現(xiàn)在可能你已經(jīng)發(fā)現(xiàn)問題了。被模糊的圖片從頂部往下滑動(dòng)——而我們的本意是希望模糊效果從上往下滑(并不是圖片本身)。

對(duì)其背景圖片

此處我們需要對(duì)靜態(tài)模糊效果使用一些技巧。當(dāng)出現(xiàn)菜單時(shí),我們需要利用背景來將模糊效果對(duì)其。所以在這里我們不是對(duì)image view做移動(dòng)處理,而是需要對(duì)image view做擴(kuò)展處理,從0開始擴(kuò)展至image view的全尺寸。這樣就可以確保菜單打開時(shí),圖片依然保留在原位。

在show方法中,我們已經(jīng)將菜單打開至全尺寸了,所以現(xiàn)在只需要將contentRect的高度設(shè)置為0即可(當(dāng)image view首次創(chuàng)建并隱藏的時(shí)候)。

將下面的代碼添加至DropDownMenuController.m文件的viewDidLoad方法中——在_blurView初始化的下方:

  1. _blurView.layer.contentsRect = CGRectMake(0.0f, 0.0f, 1.0f, 0.0f); 

同時(shí),在相同的一個(gè)文件中,將下面的代碼添加到animation block的尾部:

  1. _blurView.layer.contentsRect = CGRectMake(0.0f, 0.0f, 1.0f, 0.0f); 

contentRect屬性是可以動(dòng)畫方式設(shè)置的。因此在動(dòng)畫期間會(huì)rect會(huì)自動(dòng)的插補(bǔ)上。

編譯并運(yùn)行程序。可以看到,問題已經(jīng)解決:

這樣看起來自然多了?,F(xiàn)在我們已經(jīng)有一個(gè)具有模糊背景的滑動(dòng)菜單了。

現(xiàn)在是時(shí)候把動(dòng)畫所需時(shí)間調(diào)整一下了(為了更好的效果,其實(shí)之前設(shè)置的值是為了測(cè)試所用):設(shè)置為0.25秒,接著在updateBlur方法中將_blurFilter.blurRadiusInPixels設(shè)置為4.0f。

編譯并運(yùn)行程序,多次打開菜單,看看效果如何:

實(shí)時(shí)模糊

實(shí)時(shí)模糊涉及到的技術(shù)具有一定的難度,有些難點(diǎn)需要解決才行。為了有效的進(jìn)行實(shí)時(shí)模糊,我們需要不停(每秒60幀)的截屏、模糊計(jì)算和顯示。使用GPUImage每秒中處理60張圖片(模糊并顯示圖片)一點(diǎn)問題都沒有。

真正棘手的問題是什么呢?如何實(shí)時(shí)的截取屏幕圖片,信不信由你!

由于截取的屏幕是主用戶界面,所有必須使用CPU的主線程來截屏,并將其轉(zhuǎn)換為一幅圖片。

提醒:如果事物每秒鐘的變化速度在46幀以上,那么人眼就無法識(shí)別出來了。這對(duì)于開發(fā)者來說也是一種解脫——現(xiàn)代處理器在各幀之間可以完成更多的大量工作。

線程中簡潔的分支

當(dāng)運(yùn)行程序時(shí),會(huì)執(zhí)行大量的指令列表。每一個(gè)指令列表都運(yùn)行在各自的線程中,而我們又可以在多個(gè)線程中并發(fā)運(yùn)行各自的指令列表。一個(gè)程序在主線程中 開始運(yùn)行,然后會(huì)根據(jù)需要,創(chuàng)建新的線程,并在后臺(tái)執(zhí)行線程。如果之前你并沒有管理過多線程,你可能在寫程序的時(shí)候總是在主線程中執(zhí)行指令。

主線程主要處理與用戶的交互,以及界面的更新。確保主線程的響應(yīng)時(shí)間是非常關(guān)鍵的。如果在主線程上做了太多的任務(wù),你會(huì)明顯的感覺到主界面響應(yīng)遲鈍。

如果你曾經(jīng)使用過Twitter貨Facebook,并滾動(dòng)操作過它里面的內(nèi)容,你可能已經(jīng)感覺到后臺(tái)線程在執(zhí)行操作了——在滾動(dòng)的過程中,并不是所有的個(gè)人圖片立即顯示出來,滾動(dòng)過程中,程序會(huì)啟動(dòng)后臺(tái)線程來獲取圖片,當(dāng)圖片獲取成功之后,再顯示到屏幕中。

如果不使用后臺(tái)線程,那么table view的滾動(dòng)過程中,如果在主線程上去獲取個(gè)人圖片,會(huì)感覺到table view被凍結(jié)住了。由于圖片的獲取需要一些時(shí)間,所以最好將這樣耗時(shí)的操作讓后臺(tái)線程來做,這樣就能對(duì)用戶界面做平滑的操作和響應(yīng)了。

那么對(duì)本文的程序有什么影響呢?之間介紹了,UIView的截屏APIs操作必須在主線程中運(yùn)行。這就意味著每次截屏?xí)r,整個(gè)用戶界面都會(huì)被凍結(jié)中。

對(duì)于靜態(tài)模糊效果時(shí),由于這個(gè)截屏操作很快,你不會(huì)感覺到界面的凍結(jié)。并且只需要截屏一次。然而在實(shí)時(shí)模糊效果中需要每秒中截屏60次。如果在主線程中做這樣頻繁的截屏操作,那么animation和transition會(huì)變得非常的遲鈍。

更糟糕的時(shí),如果用戶界面復(fù)雜度增加,那么在截屏過程中就需要消耗更多的時(shí)間,那么就會(huì)導(dǎo)致整個(gè)程序無法使用了!

那么怎么辦呢!

一些潛在的實(shí)時(shí)模糊方案

這里有一個(gè)關(guān)于實(shí)時(shí)模糊方案:源代碼開源的live blur libraries,它通過降低截屏的速度來實(shí)現(xiàn)實(shí)時(shí)模糊效果,并不是使用每秒截屏60次,可能是20、30或者40次。即使看起來沒有多大區(qū)別,但是你的眼睛還是能發(fā)現(xiàn)一定的遲鈍——模糊效果并沒有跟程序的其它部分同步起來——這樣一來,界面看起會(huì)沒有模糊效果更加的糟糕。

實(shí)際上蘋果在它們自己的一些程序中處理實(shí)時(shí)模糊并不存在類似的問題——但是蘋果并沒有公開相關(guān)的API。在iOS 7中UIView的截屏方法,相比于舊方法,性能有了很大的提升,但還是不能滿足實(shí)時(shí)模糊的需求。

一些開發(fā)者利用UIToolbar的模糊效果來做一些不好的操作。沒錯(cuò),這是有效果的,但是強(qiáng)烈建議不要在程序中使用它們。雖然這不是私有API,但是這并不算是一種可行的方法,蘋果也可能會(huì)reject你的程序。也就是說在,在之后的iOS 7版本中,并不能保證還能正常使用。

蘋果可以在任何時(shí)候?qū)IToolBar做出修改,或許你的程序就有問題了。在iOS 7.0.3更新中,蘋果的修改已經(jīng)影響到UIToolbar和UINavigationBar了,有些開發(fā)者也因此報(bào)告出利用相關(guān)模糊效果已經(jīng)失效了!所以最好不要陷入這樣潛在的陷阱里面!

一個(gè)折中的方法——對(duì)視頻實(shí)時(shí)模糊

OK,此時(shí)你可能在想,要想在程序中做到實(shí)時(shí)模糊是不可能的了。那么還有什么方法可以突破限制,做到實(shí)時(shí)模糊效果呢?

在許多場(chǎng)景中,靜態(tài)模糊是可以接受的。上一節(jié)中,我們對(duì)view做適當(dāng)?shù)男薷?,讓用戶看起來是?duì)背景圖做的實(shí)際模糊處理。當(dāng)然,這對(duì)于靜止不動(dòng)的背景是合適的,并且還可以在模糊背景上實(shí)現(xiàn)一些不錯(cuò)的效果。

我們可以做一些實(shí)驗(yàn),看看能不能找到一些效果來實(shí)現(xiàn)之前無法做到的實(shí)時(shí)模糊效果呢?

有一個(gè)方法可以試試:對(duì)實(shí)時(shí)視頻做模糊處理,雖然截屏是一個(gè)非常大的瓶頸,但是GPUImage非常的強(qiáng)大,它能夠?qū)σ曨l進(jìn)行模糊(無論是來自攝像頭的視頻或者已經(jīng)錄制好的視頻,都沒問題)。

利用GPUImage對(duì)視頻進(jìn)行模糊處理

利用GPUImage對(duì)視頻的模糊處理與圖片的模糊處理類似。針對(duì)圖片,我們實(shí)例化一個(gè)GPUImagePicture,然后將其發(fā)送給GPUImageiOSBlurFilter,接著再將其發(fā)送給GPUImageView。

類似的方法,對(duì)于視頻,我們使用GPUImageVideoCamera或GPUImageMovie,將后將其發(fā)送給GPUImageiOSBlurFilter,接著再將其發(fā)送給一個(gè)GPUImageView。GPUImageVideoCamera用于設(shè)備中的實(shí)時(shí)攝像頭,而GPUImageMovie用于已經(jīng)錄制好的視頻。

在我們的starter工程中,已經(jīng)實(shí)例化并配置好了GPUImageVideoCamera?,F(xiàn)在的任務(wù)是將播放和錄制按鈕的灰色背景替換為視頻的實(shí)時(shí)濾鏡效果。

首先是將此處提供的灰色背景實(shí)例UIView替換為GPUImageView。完成之后,我們需要調(diào)整每個(gè)view的contentRect(基于view的frame)。

這聽起來對(duì)每個(gè)view都需要做大量的工作。為了讓任務(wù)變得簡單,我們創(chuàng)建一個(gè)GPUImageView的子類,并把自定義的代碼放進(jìn)去,以便重用。

打開File/New/File…,然后選擇iOS/Cocoa Touch/Objective-C class,如下所示:

將類命名為BlurView,繼承自GPUImageView,如下圖所示:

打開ViewController.m文件,將下面的import添加到文件頂部:

  1. #import "BlurView.h" 

還是在ViewController.m中,在@implementation中找到_recordView和_controlView的聲明,將其修改為BlurView類型,如下所示:

  1. BlurView *_recordView; //Update this! 
  2. UIButton *_recordButton; 
  3. BOOL _recording; 
  4.  
  5. BlurView *_controlView; //Update this too! 
  6. UIButton *_controlButton; 
  7. BOOL _playing; 

然后按照如下代碼修改viewDidLoad方法:

  1. _recordView = [[BlurView alloc] initWithFrame: 
  2.                 CGRectMake(self.view.frame.size.height/2 - 50, 250, 110, 60)]; //Update this! 
  3. //_recordView.backgroundColor = [UIColor grayColor]; //Delete this! 
  4.  
  5. _recordButton = [UIButton buttonWithType:UIButtonTypeCustom]; 
  6. _recordButton.frame = CGRectMake(5, 5, 100, 50); 
  7. [_recordButton setTitle:@"Record" forState:UIControlStateNormal]; 
  8. [_recordButton setTitleColor:[UIColor redColor] forState:UIControlStateNormal]; 
  9. [_recordButton setImage:[UIImage imageNamed:@"RecordDot.png"] forState:UIControlStateNormal] ; 
  10. [_recordButton addTarget:self 
  11.                   action:@selector(recordVideo) 
  12.         forControlEvents:UIControlEventTouchUpInside]; 
  13.  
  14. [_recordView addSubview:_recordButton]; 
  15. _recording = NO; 
  16.  
  17. _recordView.hidden = YES; 
  18. [self.view addSubview:_recordView]; 
  19.  
  20.  
  21. _controlView = [[BlurView alloc] initWithFrame: 
  22.                  CGRectMake(self.view.frame.size.height/2 - 40, 230, 80, 80)]; //Update this! 
  23. //_controlView.backgroundColor = [UIColor grayColor]; //Delete this! 

接著,需要?jiǎng)?chuàng)建模糊圖片,將其顯示到上面構(gòu)建的image view中?;氐紷implementation中,將下面的兩個(gè)聲明添加進(jìn)去:

  1. GPUImageiOSBlurFilter *_blurFilter; 
  2. GPUImageBuffer *_videoBuffer; 

現(xiàn)在你已經(jīng)知道GPUImageiOSBlurFilter的作用了,那么GPUImageBuffer的作用是什么呢?它的任務(wù)是獲取視頻的輸出,并獲取每一幀,這樣我們就可以方便的對(duì)其做模糊處理。一個(gè)額外的好處就是它可以提升程序的性能!

一般來說,視頻輸出的內(nèi)容會(huì)通過模糊濾鏡處理,然后發(fā)送到背景視圖中(被顯示出來)。不過,在這里使用buffer的話,發(fā)送到buffer的視頻輸出內(nèi)容,會(huì)被分為背景視圖和模糊濾鏡。這樣可以對(duì)視頻的輸出顯示做到平滑處理。

將下面的代碼添加到viewDidLoad方法的頂部(在super調(diào)用的后面):

  1. _blurFilter = [[GPUImageiOSBlurFilter alloc] init]; 
  2.  
  3. _videoBuffer = [[GPUImageBuffer alloc] init]; 
  4. [_videoBuffer setBufferSize:1]; 

還是在同一個(gè)文件中,將如下高亮顯示的語句添加到useLiveCamera方法中:

  1. -(void)useLiveCamera 
  2.     if (![UIImagePickerController isSourceTypeAvailable: UIImagePickerControllerSourceTypeCamera]) { 
  3.         UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"No camera detected" 
  4.                                                         message:@"The current device has no camera" 
  5.                                                        delegate:self 
  6.                                               cancelButtonTitle:@"Ok" 
  7.                                               otherButtonTitles:nil]; 
  8.         [alert show]; 
  9.         return
  10.     } 
  11.  
  12.     _liveVideo = [[GPUImageVideoCamera alloc] initWithSessionPreset:AVCaptureSessionPreset1280x720 
  13.                                                      cameraPosition:AVCaptureDevicePositionBack]; 
  14.     _liveVideo.outputImageOrientation = UIInterfaceOrientationLandscapeLeft; 
  15.  
  16.     [_liveVideo addTarget:_videoBuffer];           //Update this 
  17.     [_videoBuffer addTarget:_backgroundImageView]; //Add this 
  18.     [_videoBuffer addTarget:_blurFilter];          //And this 
  19.     [_blurFilter addTarget:_recordView];           //And finally this 
  20.  
  21.     [_liveVideo startCameraCapture]; 
  22.  
  23.     _recordView.hidden = NO; 
  24.     _controlView.hidden = YES; 

上面的模糊背景是用于錄制按鈕的。對(duì)于播放按鈕也要做類似的處理。

將下面的代碼添加到loadVideoWithURL:方法中(在_recordedVideo.playAtActualSpeed = YES;之后):

  1. [_recordedVideo addTarget:_videoBuffer]; 
  2. [_videoBuffer addTarget:_backgroundImageView]; 
  3. [_videoBuffer addTarget:_blurFilter]; 
  4. [_blurFilter addTarget:_controlView]; 

編譯并運(yùn)行程序,打開錄制操作,看看情況如何:

好消息是看起來基本正常!壞消息是整個(gè)屏幕被縮放到錄制按鈕中去了。這個(gè)問題跟之前遇到的類似。我們需要給BlurView這是適當(dāng)?shù)腸ontentRect。

打開BlurView.m,用下面的代碼替換掉initWithFrame:方法:

  1. - (id)initWithFrame:(CGRect)frame 
  2.     self = [super initWithFrame:frame]; 
  3.     if (self) { 
  4.         CGRect deviceSize = [UIScreen mainScreen].bounds; 
  5.         self.layer.contentsRect = CGRectMake(frame.origin.x/deviceSize.size.height, 
  6.                                              frame.origin.y/deviceSize.size.width, 
  7.                                              frame.size.width/deviceSize.size.height, 
  8.                                              frame.size.height/deviceSize.size.width); 
  9.         self.fillMode = kGPUImageFillModeStretch; 
  10.     } 
  11.     return self; 

contentRect的每個(gè)參數(shù)必須在0.0f和1.0f之間。在這里只需要利用view的位置除以屏幕的size,得到的值即可。

編譯并運(yùn)行程序,看看效果如何:

恭喜!至此已經(jīng)完成了靜態(tài)模糊和實(shí)時(shí)視頻模糊的實(shí)現(xiàn)。現(xiàn)在你已經(jīng)完全可以在程序中添加iOS 7的模糊效果啦!

何去何從?

可以在這里下載到完整的工程。

本文不僅指導(dǎo)你在程序中使用iOS 7的模糊效果,還介紹了如何使用GPUImage框架,這個(gè)框架也是我非常希望你能看到的東西。重要的是,本文指出了為什么要使用模糊,什么時(shí)候使用模糊效果是合適的,這在iOS 7的新設(shè)計(jì)語言中是一個(gè)關(guān)鍵的概念。當(dāng)然也希望在未來的版本中,蘋果能夠?qū)⑾嚓P(guān)APIs提供給開發(fā)者使用,不過在那之前,GPUImage是一個(gè)不錯(cuò)的替代品。

責(zé)任編輯:閆佳明 來源: beyondvincent
相關(guān)推薦

2023-05-26 07:08:05

CSS模糊實(shí)現(xiàn)文字

2017-01-17 16:45:35

githubinstagramandroid

2013-06-13 13:32:04

iOS7WWDC蘋果

2015-06-18 10:33:02

iOS粘性動(dòng)畫

2021-08-30 06:20:39

CSS 技巧3D 效果

2011-07-22 18:20:04

IOS View 動(dòng)畫

2011-07-28 09:49:50

IOS IOS 4 UI

2011-07-22 14:18:04

iOS 文件

2023-11-23 12:43:53

人臉識(shí)別程序

2015-08-06 11:25:25

iOS9多任務(wù)iCarousel

2010-09-09 11:16:06

CSS交互

2010-09-17 10:08:18

SQL中case wh

2011-08-22 14:21:24

iPhone開發(fā)UIView Anim

2011-08-12 14:04:53

iPhone動(dòng)畫

2011-08-16 17:13:02

Cocos2DFruit Ninja

2010-10-29 16:41:12

Oracle模糊查詢

2010-08-24 14:10:44

div style

2011-11-16 15:47:04

AdobeAIRiOS應(yīng)用

2011-07-08 10:15:15

IPhone 動(dòng)畫

2010-07-01 15:26:05

SQL Server
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)