Core Animation 之 View

最近切實的感覺到了本身在界面這一塊基礎原理的匱乏,所以決定深刻學習瞭解一下Core Animation, 可是在學習Core Animation的開端,不由又懷疑本身,UIWindow是什麼?它的做用你真的明白嗎?爲何要有UIWindow呢,若是沒有的話行不行呢?一系列問題層出不窮,所以我決定在研究學習Core Animation以前去學習學習蘋果的官方文檔View Programming Guide for iOS, 那麼在開始的這一篇我只會去寫一寫我對於UIView的學習,總結,應該還會有一些問題,但願和你們一塊兒交流交流。html


可能在文中會用到不一樣的詞,可是他們是一個含義:
view == view對象 == 視圖對象 == UIView對象
window == window對象 == 視窗對象 == UIWindow對象

(注:UIWindow的基類是UIView)
複製代碼

View and Window

View究竟是什麼?View對象在屏幕上定義了一塊矩形的區域而且處理這個區域內部的事件。在UIKIT中,每個UIView對象都有一個CALayer對象的layer屬性,layer對象負責視圖內容的繪製,處理視圖動畫相關的事宜。視圖由layer繪製,動畫也由layer處理,那麼UIView對象作什麼呢?其實UIView對象能夠理解爲是對layer的封裝,提供了處理觸摸事件的具體功能以及Core Animation底層方法的接口。面試

什麼是Core Animation呢?

image

Core Animation 是是iOS和OSX上可用的圖形渲染和動畫基礎結構,用於爲應用的視圖和其餘視覺元素設置動畫。它把大量的繪圖工做交給GPU去加速渲染,因此能夠實現高幀率高流暢度的動畫效果,並不會去負載CPU而使得手機卡頓。數組

Core Animation 並非一個繪圖系統,而是使用硬件去合成和處理視圖內容的基礎框架,而這個基礎框架的核心就是layer對象,那麼layer又是如何對視圖內容進行處理的呢?它會將其轉化爲GPU便宜處理的bitmap,而後GPU再去執行相應的操做,大多數APP中layer都是用來管理視圖內容的,可是也能夠去單獨的建立獨立的layer。bash

那麼如今能夠對Core Animation能夠進行一些底層的瞭解了:markdown

Core Animation是一套包含圖形繪製、投影、動畫的Objective-C類集合,該框架包含在QuartzCore.framework中. app

image

上圖的OpenGL ES是一個C語言寫的很是底層的圖形處理框架。是個移動設備上繪製2D和3D計算機圖形的標準的開源庫,普遍地被用在遊戲的圖形的繪製上負責直接驅動GPU,效率很高,可是使用起來很複雜。框架

Core Animation的核心就是對於OpenGL ES的抽象,它並不作渲染工做,而是使用OpenGL ES的功能,讓GPU去處理渲染。從上圖的結構能夠知道Core Graphic 的繪製是須要消耗CPU的,而Core Animation渲染消耗的是GPU。Core Animation最繁重的任務是去判斷出哪些layer須要被從新繪製,而OpenGL ES要作的就是將layer合併、顯示在屏幕上。ide

工做的流程圖

image

Core Graphic, Core Animation, Core Image是三個不一樣的核心庫,它們之間可能會有交互,可是不存在一個包含的關係。上圖是它們的工做流程圖oop

當你設置一個 layer 的內容爲 CGImageRef 時,Core Animation 會建立一個 OpenGL 紋理,並確保在這個圖層中的位圖被上傳到對應的紋理中。以及當你重寫 -drawInContext 方法時,Core Animation 會請求分配一個紋理,同時確保 Core Graphics 會將你所作的(即你在drawInContext中繪製的東西)放入到紋理的位圖數據中。一個layer的屬性和 CALayer 的子類會影響到 OpenGL 的渲染結果,許多底層的 OpenGL ES 行爲被簡單易懂地封裝到 CALayer 概念中。佈局

回到主題 -> UIView

好了,走偏了咱們仍是繼續聊一聊UIView吧。

視圖的層次結構

每個父視圖都會維持一個子視圖數組,在數組最後一個子視圖就是最上層的子視圖,若是子視圖重疊了,數組中靠後的子視圖位於上方,父視圖和子視圖的關係會影響子視圖的顯示,在代碼中常見的有兩個場景

//1. 要求子視圖不透明,父視圖透明
layerView.backgroundColor = UIColor.black.withAlphaComponent(0.1)

//2. scrollView自動調整子視圖size如何解決?
let scrollView = UIScrollView.init(frame: CGRect.init(x: 0, y: 0, width: 100, height: 100))
scrollView.autoresizesSubviews = false
複製代碼
視圖的響應事件

當談及UIView和CALayer的主要區別時,最重要的區別就是UIView是能夠響應事件的,那麼View是如何響應事件的呢?簡單來說分爲兩步:

  1. 尋找發生觸摸事件的視圖(從上往下)
  • 發生觸摸事件以後,系統會將該事件加入到一個由UIApplocation管理的事件隊列中
  • UIApplication會從事件隊列中取出最靠前的事件向下分發,通常先給當前的KeyWindow
  • KeyWindow根據視圖結構依次向下分發,直到找到最合適事件處理的view
  1. 找到事件的第一響應者(從下往上)
    事件響應
  • 事件響應由最底層的View開始,若是View處理不了,就向上找父View,一直找到當前Window,若是Window處理不了,會傳給UIApplication處理,若是UIApplication處理不了就由UIApplecation的delegate處理。
視圖的繪製週期

當view第一次在屏幕上顯示的時候,系統會繪製它的內容,而後系統會截取內容的快照,而且將快照做爲視圖的可見外觀,若是你永遠不改變視圖的內容,那麼視圖的繪製代碼永遠不會改變。若是更改了視圖的內容,不用直接從新繪製,而是使用setNeedsDisplay或者setNeedsDisplayInRect方法是視圖無效。這些方法會告知視圖內容以及改變而且須要在下一次進行重繪。若是須要立馬重繪,那麼須要使用layoutIfNeeded方法。

  • 更改了Frame,或者Bounds的寬和高
  • 分配了包含縮放係數的transform屬性
    contendMode
視圖的伸縮

有時候只須要可視視圖的一部份能夠被伸縮,這個在設置按鈕背景圖片的場景是很是常見的,那麼如何讓這個常見的需求實現呢?使用resizableImageWithCapInsets:方法,能夠肯定可縮放的視圖的內容,以下:

var image = UIImage.init(named: "image.png")
image = image?.resizableImage(withCapInsets: UIEdgeInsetsMake(10, 10, 10, 10), resizingMode: UIImageResizingMode.stretch)
let imageview = UIImageView.init(image: image)
複製代碼

伸縮背景的效果以下:(使四個角不變,中心進行縮放)

stretch

視圖能夠實現的動畫

視圖能夠實現的動畫效果的屬性以下:

  • frame
  • bounds
  • center
  • transform
  • alpha
  • background
座標形狀和座標系統

UIKit的座標系以屏幕的左上角爲原點

座標
若是是OpenGL ES或者是Core Graphic的話是以屏幕的左下角爲座標原點的一個笛卡爾座標系

視圖須要注意的幾點
  • 在修改視圖的transform的時候,每一種變換都是針對視圖中心而言的
  • 儘可能少的使用drawRact繪製
  • 只要有可能就顯示聲明視圖爲Opaque

回到主題之Window

那麼window的職責是什麼,爲何要有Window的存在呢?沒有Window行不行呢? 下面試官網的原文

* It contains your application’s visible content.
* It plays a key role in the delivery of touch events to your views and other application objects.
* It works with your application’s view controllers to facilitate orientation changes.
複製代碼

若是把viewController拿出來看,咱們知道viewController通常對應着相應的應用內部模塊,若是要和其餘的應用交互呢?這個就須要靠Window了;固然還有一點是屏幕的方向,這個也是經過Window來控制的!

涉及窗口的任務

在大多數時候,咱們只須要在didFinishLaunchingWithOptions:中初始化window以後就不會去涉及其餘的window操做了,若是使用IB的話,連window的初始化也沒有必要了;可是在實際的代碼過程當中仍然可能會涉及兩個任務

  • 使用窗口對象將點和矩形轉換爲窗口的本地座標系統或從窗口的本地座標系統轉換點和矩形
  • 經過窗口的通知來追蹤窗口的變化
    • UIWindowDidBecomeVisibleNotification
    • UIWindowDidBecomeHiddenNotification
    • UIWindowDidBecomeKeyNotification
    • UIWindowDidResignKeyNotification
關於不一樣的屏幕

其實吧,分屏是一個很是常見的功能,若是接觸過度屏的話就須要去仔細的研究研究分屏相關的代碼,這一塊也是和UIWindow息息相關的。

回到主題之再談View

關於視圖的層次結構
  • 使用tag 使用viewWithTag:搜索對應的視圖要比從父視圖的子視圖結構中搜索要快
  • 調整視圖的層級結構(使用調整層級的方法比移除子視圖而後從新插入更快) bringSubviewToFront: sendSubviewToBack: exchangeSubviewAtIndex:withSubviewAtIndex:
自動調整尺寸自動化調整佈局變動

在佈局中有一個很討厭的事情,就是明明你的frame都已經設定好了,可是依然在運行的時候就會發生變化,其實不少這種狀況的緣由就是被添加的子視圖的frame會隨着父視圖的frame的變化而發生變化;其二就是有時候子視圖須要保持和父視圖一致的大小變換,或者維持原有的尺寸不變,這個時候就須要視圖的自動化調整佈局了。

父視圖的 autoresizesSubviews 屬性決定全部子視圖是否須要調整。若是屬性設置爲 YES,視圖會使用每一個子視圖的 autoresizingMask 屬性決定子視圖的位置和尺寸。每一個子視圖的尺寸變動會對它們的子視圖觸發一樣的佈局調整。每個子視圖的autoresizingMask屬性如何決定呢?

  • UIViewAutoresizingFlexibleHeight 當父視圖高度變動時視圖的高度也變動。若是這個常量沒被包含,視圖的高度將不會變動。
  • UIViewAutoresizingFlexibleWidth 當父視圖寬度變動時視圖的寬度也變動。若是這個常量沒被包含,視圖的寬度將不會變動。
  • UIVIewAutoresizingFlexibleLeftMargin 視圖的左邊緣和父視圖的左邊緣之間的距離根據須要增加或縮短。若是這個常量沒有設置,視圖的左邊緣與父視圖的左邊緣之間的距離保持固定。
  • UIViewAutoresizingFlexibleRightMargin 視圖的右邊緣和父視圖的右邊緣之間的距離根據須要增加或縮短。若是這個常量沒有設置,視圖的右邊緣與父視圖的右邊緣之間的距離保持固定。
  • UIViewAutoresizingFlexibleBottomMargin 視圖的底部邊緣和父視圖的底部邊緣之間的距離根據須要增加或縮短。若是這個常量沒有設置,視圖的底部邊緣與父視圖的底部邊緣之間的距離保持固定。
  • UIViewAutoresizingFlexibleTopMargin 視圖的頂部邊緣和父視圖的頂部邊緣之間的距離根據須要增加或縮短。若是這個常量沒有設置,視圖的頂部邊緣與父視圖的頂部邊緣之間的距離保持固定。
視圖初始化

在使用code時: initWithFrame: 在使用nib時: initWithCoder: -> awakeFromNib

視圖的繪製

在實際的操做中,要儘可能少的使用drawRect:方法,若是要使用此方法,明確它的職責:繪製內容。使用的時候要配置環境,繪製內容,儘量快結束繪製。 提升繪製性能的兩個小tips:

  1. 若是你清楚你的視圖繪製代碼老是以不透明的內容覆蓋視圖的整個表面,你能夠設置視圖的 opaque 屬性爲 YES 提高系統性能。當你標記了視圖爲不透明時,UIKit 會避開在位於視圖背後的繪製代碼。這不是惟一縮減繪製花費的時間的方式但也最大限度減小視圖和其餘內容之間的合成工做。所以,若是你的視圖的內容是徹底不透明的那麼應該設置這個屬性爲 YES。若是你的視圖不能保證內容老是不透明的,你應該設置這個屬性爲 NO。
  2. 另外一個提高繪製性能的方式,尤爲在滾動過程當中,是設置視圖的 clearsContextBeforeDrawing 屬性爲 NO。當這個屬性設置爲 YSE 時,在你的 drawRect: 方法調用以前 UIKit 把將要被 drawRect: 方法更新的區域自動填充爲半透明的黑色。設置這個屬性爲 NO 會消除填充操做的開銷但會加劇你的應用程序經過 drawRect: 方法填充更新矩形的內容的負擔。
視圖的動畫

這個就很是常見了,大多使用的是UIView自帶的Block動畫,這個就不在細聊了,有一點想提一提,就是普通的動畫只能是從A -> B兩種狀態之間進行切換,若是加上交互的話,動畫就會變得不流暢了,因此Apple在iOS10添加了一個新的特性:UIViewPropertyAnimator ,這個在後面再聊吧。

總結

也不知道爲何寫了這麼多,其實寫的時候,主要仍是不少東西都是有關聯的,一旦深刻其實還會有很是多的東西在裏面,以上是個人學習和一些總結。若是寫的很差,歡迎指出問題,這樣我就能夠慢慢改進了,下一篇,我會用比較短的篇幅聊一聊UIViewController,而後就正式的進入個人Core Animation的學習過程當中了。

相關文章
相關標籤/搜索