iOS中實現模糊效果教程
iOS 7在視覺方面有許多改變,其中非常吸引人的功能之一就是在整個系統中巧妙的使用了模糊效果。許多第三方應用程序已經采用了這樣的設計細節,并以各種奇妙的和具有創造性的方式使用它。
本文將通過幾種不同的技術來實現iOS 7中的模糊效果,當然,這一切都利用了一個名為GPUImage的框架。
GPUImage是由Brad Larson創建的,它利用GPU,使在圖片和視頻上應用不同的效果和濾鏡變得非常的容易,同時它還擁有出色的性能,并且它的性能要比蘋果內置的相關APIs出色。
注意:本文需要一臺物理設備來編譯并運行示例程序(在模擬器上無法使用)。同樣還需要一個iOS開發者賬號。如果你還沒有開發者賬號的 話,可以來[這里](https://developer.apple.com/)注冊一個。注冊為開發者之后,會有許多福利喲,例如可以使用物理設備來 開發程序,提前獲得蘋果的相關測試版程序,以及大量的開發資源。
iOS中利用GPUImage實現模糊效果
下面我們先來看看本文的目錄結構:
- 開始
- 為什么要是用模糊效果
- 深度引導
- 上下文
- 關注度
- 添加靜態的模糊效果
- 創建截圖Category
- 利用斷點測試截屏圖片
- 顯示截屏圖片
- 設置contentsRect
- 重置模糊濾鏡
- 對其背景圖片
- 實時模糊
- 線程中簡潔的分支
- 一些潛在的實時模糊方案
- 一個折中的方法——對視頻實時模糊
- 利用GPUImage對視頻進行模糊處理
- 何去何從?
開始
首先先來這里下載本文的starter工程,并將其解壓出來。
用Xcode打開Video Blurring.xcodeproj,并將工程運行到設備中。此時看到程序的效果如下所示:
點擊屏幕左上角的菜單(三條橫紋),可以看到界面中出現兩個選項:錄制視頻和播放已有視頻。
請注意,現在所有的用戶界面都有一個灰色的背景,是不是感覺有點沉悶呢,本文我們就利用iOS 7中的模糊效果來替換掉這些沉悶的灰色背景。
為什么要是用模糊效果
除了外觀看起來很棒以外,模糊效果還可以讓程序給用戶帶來3個重要的概念:深度引導、上下文和關注度。
深度引導
在用戶界面上,模糊效果可以給用戶提供一個深度引導效果,并且有利于用戶對程序導航的理解。在之前的iOS版本中的深度引導效果是通過:三維斜面 (three-dimensional bevels)和有關澤的按鈕(反映出一個模擬的光源),而在iOS 7中是通過模糊和視差(parallax)來實現的。
這里說的視差效果,可以很明顯的觀察出來:在裝有iOS 7的設備中,將設備從一側傾斜至另一側,會發現設備中的圖標在移動(會獨立于背景)。這樣可以給用戶做出一個提示:界面是由不同的層構成的,并且重要的界 面元素是在最前面的——這也涉及到下面將要介紹的一個概念:上下文。
上下文
上下文可以讓用戶在程序內獲得一種軸承的感覺。動畫的過度效果就提供了一種非常優秀的上下文,當用戶點擊一個按鈕時,在兩個view之間利用動畫效 果來切換畫面(而不是直接顯示一個新的view),可以讓用戶知道新的view是從哪里出現的,并且可以讓用戶很容易知道如何回到上一個view。
模糊效果可以將上一個view當做背景顯示出來,盡管上一個view已經失去焦點了,不過可以給用戶提供更多的上下文:剛剛是在哪里。通知中心就是一個非常棒的例子:當拉下通知中心時,我們可以在背景中看到原來的view(即使現在正在處于通知中心界面)。
關注度
讓界面更加關注于某些選擇項上,而移除不需要的內容,讓用戶可以更加快捷的進行導航。用戶可以本能的忽略那些被模糊的界面元素,而將注意力集中到某些界面元素中。
通過本文,你將學到兩種模糊類型的實現方法:靜態模糊和動態模糊。靜態模糊代表著快照的時間點,它并不能反映被模糊界面元素的變化。大多數情況下,使用靜態模糊效果就足夠了。相反,動態模糊則是對需要模糊的背景做出實時更新。
相信看到具體的效果才是最好的,下面我們就來看看模糊效果的具體實現吧!
添加靜態的模糊效果
創建一個靜態模糊效果首先是將當前屏幕中的view轉換為一幅圖片。獲得圖片之后,只需要對圖片做模糊處理就可以了。將view轉換為一幅圖片(截屏)蘋果已經提供了一些非常棒的APIs了,并且在iOS 7中又有了新的方法可以讓截屏更加快速。
這些新的方法屬于截屏APIs中的一部分,截屏APIs不僅可以對某個view截屏,還能把整個view層次截屏,如果你希望對某個view截屏,那么可以把view中的按鈕、標簽、開關等各種view也進行截屏。
此處我們將截屏的邏輯實現到UIView的一個category中。這樣一來,我們就可以很方便快捷的將任意的view(以及view中的內容)轉換為一個圖片——也算是代碼的重用吧。
創建截圖Category
打開File/New/File…,然后選擇iOS/Cocoa Touch/Objective-C category,如下圖所示:
將這個category命名為Screenshot,并將它的category選為UIView,如下圖所示:
將下面這個方法聲明到UIView+Screenshot.h中:
-(UIImage *)convertViewToImage;
接著將如下方法添加到 UIView+Screenshot.m 中:
-(UIImage )convertViewToImage { UIGraphicsBeginImageContext(self.bounds.size); [self drawViewHierarchyInRect:self.bounds afterScreenUpdates:YES]; UIImage image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext();return image;
}</pre>
上面的方法中,首先調用了UIGraphicsBeginImageContext(),最后調用的是 UIGraphicsEndImageContext(),這兩行代碼可以理解為圖形上下文的一個事物處理過程。一個上下文可以理解為不同的概念,例如屏 幕,或者此處可以理解為一幅圖片。這里的兩行代碼起到一個離屏畫布的作用——可以將view繪制上去。
drawViewHierarchyInRect:afterScreenUpdates:方法利用view層次結構并將其繪制到當前的上下文中。
最后,UIGraphicsGetImageFromCurrentImageContext()從圖形上下文中獲取剛剛生成的UIImage。
現在,我們已經完成了category的實現,接著我們只需要在使用到的地方將其import一下即可。
如下代碼所示,將代碼添加到DropDownMenuController.m頂部:
#import "UIView+Screenshot.h"同時,將如下方法添加到相同的文件中:
-(void)updateBlur { UIImage *image = [self.view.superview convertViewToImage]; }上面的代碼確保是對superview進行截屏,而不僅僅是當前的view。不這樣做的話,截屏獲得的圖片只是menu本身。
利用斷點測試截屏圖片
為了測試截屏的效果,我們在convertViewToImage調用的下面一行添加一個斷點。這樣當命中斷點時,程序會在斷點中暫停執行,這樣我們就可以看到截屏的圖片,以此確保截屏代碼的正確性:
在測試之前還有一件事情需要做:調用上面這個方法。
找到show方法,并在addToParentViewController下面直接調用一下updateBlur:
-(void)show { [self addToParentViewController];[self updateBlur]; // Add this line CGRect deviceSize = [UIScreen mainScreen].bounds; [UIView animateWithDuration:0.25f animations:^(void){ _blurView.frame = CGRectMake(0, 0, deviceSize.size.height, MENUSIZE); _backgroundView.frame = CGRectMake(0, 0, _backgroundView.frame.size.width, MENUSIZE); }];
}</pre>
編譯并運行程序,點擊菜單按鈕,可以看到Xcode在斷點出停止了,如下所示:
在debugger左下角hand pane中選擇image,然后單擊快速查找圖標按鈕,就可以預覽剛剛的截屏啦:
如上圖所示,正是我們所預期的。
顯示截屏圖片
將截取到的圖片顯示到菜單的背景中就是小菜一碟啦。
一般來說我們都會利用UIImageView來顯示一幅圖片,而由于我們要利用GPUImage來模糊圖片,所以需要使用GPUImageView。
在這里的工程中,已經添加好了GPUImage框架,我們只需要將頭文件import一下即可。
將下面的代碼添加到DropDownMenuController.m頂部:
#import <GPUImage/GPUImage.h>注意:GPUImage被包含在一個框架中,所以在import語句中,需要利用尖括弧,而不是雙引號。
此時,有一個_blurView,類型為UIView——是菜單的灰色背景。將UIView修改為GPUImageView,如下所示:
@implementation DropDownMenuController { GPUImageView *_blurView; UIView *_backgroundView; }修改之后,Xcode會報一個warning:大意是你利用UIView進行實例化,而不是預期的GPUImageView。
可以通過下面的方法消除這個警告,在viewDidLad中修改做如下修改:
_blurView = [[GPUImageView alloc] initWithFrame:CGRectMake(0, 0, deviceSize.size.height, 0)];緊隨其后,將如下兩行代碼添加進去,并移除設置背景色的代碼:
_blurView.clipsToBounds = YES; _blurView.layer.contentsGravity = kCAGravityTop;clipToBounds屬性設置為YES,把超出_blurView范圍的子view隱藏起來,而contentsGravity確保圖片出現在image view的頂部。
由于_blurView已經用于背景了,所以此處不需要額外設置了。
接著,我們還需要聲明一個用于模糊效果的過濾器。
將如下代碼添加到DropDownMenuController.m:文件的@implementation中
GPUImageiOSBlurFilter *_blurFilter;找到之前添加的斷點,右鍵單擊,并選中Delete Breakpoint:
下面是非常重要的一步了——初始化模糊濾鏡。將如下代碼添加到DropDownMenuController.m中:
-(void)updateBlur { if(_blurFilter == nil){ _blurFilter = [[GPUImageiOSBlurFilter alloc] init]; _blurFilter.blurRadiusInPixels = 1.0f;} UIImage *image = [self.view.superview convertViewToImage];
}</pre>
注意:上面將模糊半徑設置為一個像素,這里暫時將這個值設置低一點,這樣可以確保圖片的正確定位,當一切ok之后,再增加模糊半徑即可。
下面是時候將圖片顯示到GPUImageView中了。不過并不是簡單的實例化一個UIImage,并將其添加到GPUImageView中。首先需創建一個GPUImagePicture。
將如下代碼添加到updateBlur方法的底部:
GPUImagePicture *picture = [[GPUImagePicture alloc] initWithImage:image];至此,我們獲得了一個圖片,模糊濾鏡和iamge view。
接著再將如下代碼添加到updateBlur底部:
[picture addTarget:_blurFilter]; [_blurFilter addTarget:_blurView];[picture processImage];</pre>
上面這幾行代碼,就像膠水一樣,將所有的事情關聯起來。將濾鏡當做target添加到圖片中,然后將image view當做濾鏡的target。
上面代碼對圖片的處理全程發生在GPU上,也就是說當進行模糊計算和顯示時,并不會影響到用戶界面。當處理結束時,會把圖片顯示到image view上面。
編譯并運行程序,點擊菜單按鈕,可以看到如下類似畫面:
上面的圖片看起來是不是有點奇怪?看到的圖片被縮放到適配到菜單視圖中了。要對此做出修正,我們需要指定圖片的哪一部分需要顯示在GPUImageView中——也就是處理截屏視圖的上半部分。
設置contentsRect
按照如下代碼所示修改DropDownMenuController.m文件中的show方法:
-(void)show { [self addToParentViewController];[self updateBlur]; CGRect deviceSize = [UIScreen mainScreen].bounds; [UIView animateWithDuration:0.25f animations:^(void){ _blurView.frame = CGRectMake(0.0f, 0.0f, deviceSize.size.height, MENUSIZE); _backgroundView.frame = CGRectMake(0.0f, 0.0f, _backgroundView.frame.size.width, MENUSIZE); _blurView.layer.contentsRect = CGRectMake(0.0f, 0.0f, 1.0f, MENUSIZE / 320.0f); // Add this line! }];
}</pre>
通過指定_blurView.layer.contentsRect來定義一個矩形,在單元坐標空間(unit coordinate space)中,表示只使用layer content的一部分。
編譯并運行程序,點擊菜單按鈕,會看到如下圖所示效果:
雖然已經使用了圖片的一部分,看起來還是不正確,這是因為它的縮放比例還不適合!此處還缺少對正確內容的縮放。
將下面這行代碼添加到show方法中動畫block的尾部:
_blurView.layer.contentsScale = (MENUSIZE / 320.0f) * 2;contentsScale屬性聲明了layer在邏輯坐標空間(以點為單位)和物理坐標空間(以像素為單位)之間的映射關系。更高比例因子表示在渲染layer時,一個點代表著多個像素點。
編譯并運行程序,點擊菜單按鈕,可以看到縮放比例已經正常了:
沒錯——看起來好多了!現在關閉程序,然后重新打開,ou~~發生了什么?如下圖所示:
看起來這還是有點問題。如果在對view進行animation之前將contentScale設置回2.0,會解決half bar的問題。
將如下代碼添加到DropDownMenuController.m中show方法里面的animation block上面:
_blurView.layer.contentsScale = 2.0f;編譯并運行程序,然后點擊菜單,接著關閉菜單,再打開菜單,此時菜單開起來如下所示:
現在半個尺寸的黑色box已經沒有問題了——但是現在是全尺寸的黑色box!
重置模糊濾鏡
上面問題產生的原因是由于進行了二次模糊計算。解決的方法是移除模糊濾鏡中的所有target。如果不這樣做的話,之后對濾鏡的調用不會輸出任何的內容——進而引起黑色box的問題。
按照如下代碼更新一下updateBlur方法:
-(void)updateBlur { if(_blurFilter == nil){ _blurFilter = [[GPUImageiOSBlurFilter alloc] init]; _blurFilter.blurRadiusInPixels = 1.0f; }UIImage *image = [self.view.superview convertViewToImage]; GPUImagePicture *picture = [[GPUImagePicture alloc] initWithImage:image]; [picture addTarget:_blurFilter]; [_blurFilter addTarget:_blurView]; [picture processImageWithCompletionHandler:^{ [_blurFilter removeAllTargets]; }];
}</pre>
上面的代碼用processImageWithCompletionHandler:替換了processImage方法。這個新的方法有一個 completion block,當image 處理結束時,會運行這個block。一旦image處理結束,我們就可以安全的將濾鏡中的target全部移除。
編譯并運行程序,點擊菜單,檢查一下黑色box問題是不是已經解決掉了:
多次打開和關閉菜單,確保之前的那個bug已經解決掉啦!
現在仔細觀察一下打開菜單的模糊效果——有些東西看起來不正確。為了更加明顯的觀察到問題,我們減慢動畫的時間,讓其慢慢的移動。
在show方法中,將animation bloc的持續時間修改為10.0f。
編譯并運行程序,點擊菜單,然后觀察一下菜單出場的慢動作:
恩,現在可能你已經發現問題了。被模糊的圖片從頂部往下滑動——而我們的本意是希望模糊效果從上往下滑(并不是圖片本身)。
對其背景圖片
此處我們需要對靜態模糊效果使用一些技巧。當出現菜單時,我們需要利用背景來將模糊效果對其。所以在這里我們不是對image view做移動處理,而是需要對image view做擴展處理,從0開始擴展至image view的全尺寸。這樣就可以確保菜單打開時,圖片依然保留在原位。
在show方法中,我們已經將菜單打開至全尺寸了,所以現在只需要將contentRect的高度設置為0即可(當image view首次創建并隱藏的時候)。
將下面的代碼添加至DropDownMenuController.m文件的viewDidLoad方法中——在_blurView初始化的下方:
_blurView.layer.contentsRect = CGRectMake(0.0f, 0.0f, 1.0f, 0.0f);同時,在相同的一個文件中,將下面的代碼添加到animation block的尾部:
_blurView.layer.contentsRect = CGRectMake(0.0f, 0.0f, 1.0f, 0.0f);contentRect屬性是可以動畫方式設置的。因此在動畫期間會rect會自動的插補上。
編譯并運行程序。可以看到,問題已經解決:
這樣看起來自然多了。現在我們已經有一個具有模糊背景的滑動菜單了。
現在是時候把動畫所需時間調整一下了(為了更好的效果,其實之前設置的值是為了測試所用):設置為0.25秒,接著在updateBlur方法中將_blurFilter.blurRadiusInPixels設置為4.0f。
編譯并運行程序,多次打開菜單,看看效果如何:
實時模糊
實時模糊涉及到的技術具有一定的難度,有些難點需要解決才行。為了有效的進行實時模糊,我們需要不停(每秒60幀)的截屏、模糊計算和顯示。使用GPUImage每秒中處理60張圖片(模糊并顯示圖片)一點問題都沒有。
真正棘手的問題是什么呢?如何實時的截取屏幕圖片,信不信由你!
由于截取的屏幕是主用戶界面,所有必須使用CPU的主線程來截屏,并將其轉換為一幅圖片。
提醒:如果事物每秒鐘的變化速度在46幀以上,那么人眼就無法識別出來了。這對于開發者來說也是一種解脫——現代處理器在各幀之間可以完成更多的大量工作。
線程中簡潔的分支
當運行程序時,會執行大量的指令列表。每一個指令列表都運行在各自的線程中,而我們又可以在多個線程中并發運行各自的指令列表。一個程序在主線程中 開始運行,然后會根據需要,創建新的線程,并在后臺執行線程。如果之前你并沒有管理過多線程,你可能在寫程序的時候總是在主線程中執行指令。
主線程主要處理與用戶的交互,以及界面的更新。確保主線程的響應時間是非常關鍵的。如果在主線程上做了太多的任務,你會明顯的感覺到主界面響應遲鈍。
如果你曾經使用過推ter貨非死book,并滾動操作過它里面的內容,你可能已經感覺到后臺線程在執行操作了——在滾動的過程中,并不是所有的個人圖片立即顯示出來,滾動過程中,程序會啟動后臺線程來獲取圖片,當圖片獲取成功之后,再顯示到屏幕中。
如果不使用后臺線程,那么table view的滾動過程中,如果在主線程上去獲取個人圖片,會感覺到table view被凍結住了。由于圖片的獲取需要一些時間,所以最好將這樣耗時的操作讓后臺線程來做,這樣就能對用戶界面做平滑的操作和響應了。
那么對本文的程序有什么影響呢?之間介紹了,UIView的截屏APIs操作必須在主線程中運行。這就意味著每次截屏時,整個用戶界面都會被凍結中。
對于靜態模糊效果時,由于這個截屏操作很快,你不會感覺到界面的凍結。并且只需要截屏一次。然而在實時模糊效果中需要每秒中截屏60次。如果在主線程中做這樣頻繁的截屏操作,那么animation和transition會變得非常的遲鈍。
更糟糕的時,如果用戶界面復雜度增加,那么在截屏過程中就需要消耗更多的時間,那么就會導致整個程序無法使用了!
那么怎么辦呢!
一些潛在的實時模糊方案
這里有一個關于實時模糊方案:源代碼開源的live blur libraries,它通過降低截屏的速度來實現實時模糊效果,并不是使用每秒截屏60次,可能是20、30或者40次。即使看起來沒有多大區別,但是你 的眼睛還是能發現一定的遲鈍——模糊效果并沒有跟程序的其它部分同步起來——這樣一來,界面看起會沒有模糊效果更加的糟糕。
實際上蘋果在它們自己的一些程序中處理實時模糊并不存在類似的問題——但是蘋果并沒有公開相關的API。在iOS 7中UIView的截屏方法,相比于舊方法,性能有了很大的提升,但還是不能滿足實時模糊的需求。
一些開發者利用UIToolbar的模糊效果來做一些不好的操作。沒錯,這是有效果的,但是強烈建議不要在程序中使用它們。雖然這不是私有API, 但是這并不算是一種可行的方法,蘋果也可能會reject你的程序。也就是說在,在之后的iOS 7版本中,并不能保證還能正常使用。
蘋果可以在任何時候對UIToolBar做出修改,或許你的程序就有問題了。在iOS 7.0.3更新中,蘋果的修改已經影響到UIToolbar和UINavigationBar了,有些開發者也因此報告出利用相關模糊效果已經失效了!所 以最好不要陷入這樣潛在的陷阱里面!
一個折中的方法——對視頻實時模糊
OK,此時你可能在想,要想在程序中做到實時模糊是不可能的了。那么還有什么方法可以突破限制,做到實時模糊效果呢?
在許多場景中,靜態模糊是可以接受的。上一節中,我們對view做適當的修改,讓用戶看起來是對背景圖做的實際模糊處理。當然,這對于靜止不動的背景是合適的,并且還可以在模糊背景上實現一些不錯的效果。
我們可以做一些實驗,看看能不能找到一些效果來實現之前無法做到的實時模糊效果呢?
有一個方法可以試試:對實時視頻做模糊處理,雖然截屏是一個非常大的瓶頸,但是GPUImage非常的強大,它能夠對視頻進行模糊(無論是來自攝像頭的視頻或者已經錄制好的視頻,都沒問題)。
利用GPUImage對視頻進行模糊處理
利用GPUImage對視頻的模糊處理與圖片的模糊處理類似。針對圖片,我們實例化一個GPUImagePicture,然后將其發送給GPUImageiOSBlurFilter,接著再將其發送給GPUImageView。
類似的方法,對于視頻,我們使用GPUImageVideoCamera或GPUImageMovie,將后將其發送給 GPUImageiOSBlurFilter,接著再將其發送給一個GPUImageView。GPUImageVideoCamera用于設備中的實時 攝像頭,而GPUImageMovie用于已經錄制好的視頻。
在我們的starter工程中,已經實例化并配置好了GPUImageVideoCamera。現在的任務是將播放和錄制按鈕的灰色背景替換為視頻的實時濾鏡效果。
首先是將此處提供的灰色背景實例UIView替換為GPUImageView。完成之后,我們需要調整每個view的contentRect(基于view的frame)。
這聽起來對每個view都需要做大量的工作。為了讓任務變得簡單,我們創建一個GPUImageView的子類,并把自定義的代碼放進去,以便重用。
打開File/New/File…,然后選擇iOS/Cocoa Touch/Objective-C class,如下所示:
將類命名為BlurView,繼承自GPUImageView,如下圖所示:
打開ViewController.m文件,將下面的import添加到文件頂部:
#import "BlurView.h"還是在ViewController.m中,在@implementation中找到_recordView和_controlView的聲明,將其修改為BlurView類型,如下所示:
BlurView _recordView; //Update this! UIButton _recordButton; BOOL _recording;BlurView _controlView; //Update this too! UIButton _controlButton; BOOL _playing;</pre>
然后按照如下代碼修改viewDidLoad方法:
_recordView = [[BlurView alloc] initWithFrame: CGRectMake(self.view.frame.size.height/2 - 50, 250, 110, 60)]; //Update this! //_recordView.backgroundColor = [UIColor grayColor]; //Delete this!_recordButton = [UIButton buttonWithType:UIButtonTypeCustom]; _recordButton.frame = CGRectMake(5, 5, 100, 50); [_recordButton setTitle:@"Record" forState:UIControlStateNormal]; [_recordButton setTitleColor:[UIColor redColor] forState:UIControlStateNormal]; [_recordButton setImage:[UIImage imageNamed:@"RecordDot.png"] forState:UIControlStateNormal] ; [_recordButton addTarget:self action:@selector(recordVideo) forControlEvents:UIControlEventTouchUpInside];
[_recordView addSubview:_recordButton]; _recording = NO;
_recordView.hidden = YES; [self.view addSubview:_recordView];
_controlView = [[BlurView alloc] initWithFrame: CGRectMake(self.view.frame.size.height/2 - 40, 230, 80, 80)]; //Update this! //_controlView.backgroundColor = [UIColor grayColor]; //Delete this!</pre>
接著,需要創建模糊圖片,將其顯示到上面構建的image view中。回到@implementation中,將下面的兩個聲明添加進去:
GPUImageiOSBlurFilter *_blurFilter; GPUImageBuffer *_videoBuffer;現在你已經知道GPUImageiOSBlurFilter的作用了,那么GPUImageBuffer的作用是什么呢?它的任務是獲取視頻的輸出,并獲取每一幀,這樣我們就可以方便的對其做模糊處理。一個額外的好處就是它可以提升程序的性能!
一般來說,視頻輸出的內容會通過模糊濾鏡處理,然后發送到背景視圖中(被顯示出來)。不過,在這里使用buffer的話,發送到buffer的視頻輸出內容,會被分為背景視圖和模糊濾鏡。這樣可以對視頻的輸出顯示做到平滑處理。
將下面的代碼添加到viewDidLoad方法的頂部(在super調用的后面):
_blurFilter = [[GPUImageiOSBlurFilter alloc] init];_videoBuffer = [[GPUImageBuffer alloc] init]; [_videoBuffer setBufferSize:1];</pre>
還是在同一個文件中,將如下高亮顯示的語句添加到useLiveCamera方法中:
-(void)useLiveCamera { if (![UIImagePickerController isSourceTypeAvailable: UIImagePickerControllerSourceTypeCamera]) { UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"No camera detected" message:@"The current device has no camera" delegate:self cancelButtonTitle:@"Ok" otherButtonTitles:nil]; [alert show]; return; }_liveVideo = [[GPUImageVideoCamera alloc] initWithSessionPreset:AVCaptureSessionPreset1280x720 cameraPosition:AVCaptureDevicePositionBack]; _liveVideo.outputImageOrientation = UIInterfaceOrientationLandscapeLeft; [_liveVideo addTarget:_videoBuffer]; //Update this [_videoBuffer addTarget:_backgroundImageView]; //Add this [_videoBuffer addTarget:_blurFilter]; //And this [_blurFilter addTarget:_recordView]; //And finally this [_liveVideo startCameraCapture]; _recordView.hidden = NO; _controlView.hidden = YES;
}</pre>
上面的模糊背景是用于錄制按鈕的。對于播放按鈕也要做類似的處理。
將下面的代碼添加到loadVideoWithURL:方法中(在_recordedVideo.playAtActualSpeed = YES;之后):
[_recordedVideo addTarget:_videoBuffer]; [_videoBuffer addTarget:_backgroundImageView]; [_videoBuffer addTarget:_blurFilter]; [_blurFilter addTarget:_controlView];編譯并運行程序,打開錄制操作,看看情況如何:
好消息是看起來基本正常!壞消息是整個屏幕被縮放到錄制按鈕中去了。這個問題跟之前遇到的類似。我們需要給BlurView這是適當的contentRect。
打開BlurView.m,用下面的代碼替換掉initWithFrame:方法:
- (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { CGRect deviceSize = [UIScreen mainScreen].bounds; self.layer.contentsRect = CGRectMake(frame.origin.x/deviceSize.size.height, frame.origin.y/deviceSize.size.width, frame.size.width/deviceSize.size.height, frame.size.height/deviceSize.size.width); self.fillMode = kGPUImageFillModeStretch; } return self; }contentRect的每個參數必須在0.0f和1.0f之間。在這里只需要利用view的位置除以屏幕的size,得到的值即可。
編譯并運行程序,看看效果如何:
恭喜!至此已經完成了靜態模糊和實時視頻模糊的實現。現在你已經完全可以在程序中添加iOS 7的模糊效果啦!
何去何從?
可以在這里下載到完整的工程。
本文不僅指導你在程序中使用iOS 7的模糊效果,還介紹了如何使用GPUImage框架,這個框架也是我非常希望你能看到的東西。重要的是,本文指出了為什么要使用模糊,什么時候使用模糊 效果是合適的,這在iOS 7的新設計語言中是一個關鍵的概念。當然也希望在未來的版本中,蘋果能夠將相關APIs提供給開發者使用,不過在那之前,GPUImage是一個不錯的替 代品。
來源:51CTO