前言
- 這一塊主要為了面試講項(xiàng)目準(zhǔn)備,盡量理論與實(shí)踐相結(jié)合吧挂签,能把搜到的資料都實(shí)現(xiàn)最好
- 目前主要是為tableView的優(yōu)化進(jìn)行研究疤祭,把涉及到的異步繪制【圖層問題】,網(wǎng)絡(luò)請(qǐng)求中的cell中空間高度緩存這些就用到最好
FPS
- FPS是圖像領(lǐng)域中的定義饵婆,是指畫面每秒傳輸幀數(shù)勺馆,通俗來講就是指動(dòng)畫或視頻的畫面數(shù)。FPS是測(cè)量用于保存侨核、顯示動(dòng)態(tài)視頻的信息數(shù)量草穆。每秒鐘幀數(shù)越多,所顯示的動(dòng)作就會(huì)越流暢搓译。通常悲柱,要避免動(dòng)作不流暢的最低是30。
- iPhone的FPS為60些己,也就是說它在一秒鐘內(nèi)會(huì)刷新60次豌鸡,每次間隔大概16.7ms,也就是說我們的iPhone每次有16.7ms的時(shí)間來處理事件
- 而如果在這個(gè)時(shí)間內(nèi)沒能成功刷新段标,就會(huì)直接丟棄這一幀涯冠,下一次一起整上去【也就是說,不會(huì)原地刷新】
- 這樣子幀的丟失反映到用到用戶體驗(yàn)上就 = APP卡頓了
CPU
- 中央處理器(CPU逼庞,central processing unit)作為計(jì)算機(jī)系統(tǒng)的運(yùn)算和控制核心蛇更,是信息處理、程序運(yùn)行的最終執(zhí)行單元
- 放在我們的iOS中赛糟,可以理解為CPU負(fù)責(zé)軟件的操作械荷,對(duì)象創(chuàng)建、對(duì)象調(diào)整虑灰、對(duì)象銷毀吨瞎、布局計(jì)算、文本計(jì)算穆咐、文本渲染颤诀、圖片解碼字旭、圖像的繪制等操作是放在CPU上執(zhí)行的
GPU
- 圖形處理器(英語:Graphics Processing Unit,縮寫:GPU)崖叫,又稱顯示核心遗淳、視覺處理器、顯示芯片心傀,是一種專門在個(gè)人電腦屈暗、工作站、游戲機(jī)和一些移動(dòng)設(shè)備(如平板電腦脂男、智能手機(jī)等)上做圖像和圖形相關(guān)運(yùn)算工作的微處理器养叛。
- 放在iOS中,GPU處理的是硬件層面的事宰翅,紋理的渲染弃甥、視圖混合、離屏渲染都是在GPU上執(zhí)行的
位圖
- 位圖是由稱作像素(圖片元素)的單個(gè)點(diǎn)組成的汁讼。這些點(diǎn)可以進(jìn)行不同的排列和染色以構(gòu)成圖樣淆攻。當(dāng)放大位圖時(shí),可以看見賴以構(gòu)成整個(gè)圖像的無數(shù)單個(gè)方塊嘿架。擴(kuò)大位圖尺寸的效果是增大單個(gè)像素瓶珊,從而使線條和形狀顯得參差不齊。然而耸彪,如果從稍遠(yuǎn)的位置觀看它艰毒,位圖圖像的顏色和形狀又顯得是連續(xù)的。用數(shù)碼相機(jī)拍攝的照片搜囱、掃描儀掃描的圖片以及計(jì)算機(jī)截屏圖等都屬于位圖丑瞧。位圖的特點(diǎn)是可以表現(xiàn)色彩的變化和顏色的細(xì)微過渡,產(chǎn)生逼真的效果蜀肘,缺點(diǎn)是在保存時(shí)需要記錄每一個(gè)像素的位置和顏色值绊汹,占用較大的存儲(chǔ)空間。常用的位圖處理軟件有Photoshop(同時(shí)也包含矢量功能)扮宠、Painter和Windows系統(tǒng)自帶的畫圖工具等西乖,Adobe Illustrator則是矢量圖軟件
像素點(diǎn)如何出現(xiàn)在屏幕上
- 先來看一張全部流程圖:
pixels-software-stack
- 這張圖從從右到左就是軟件到硬件,從App到硬件屏幕上出現(xiàn)界面的全過程
- 這里再介紹下前面沒介紹的模塊
渲染參與者
- GPU Driver:GPU驅(qū)動(dòng)軟件坛增,直接和 GPU 交流的代碼塊
- OpenGL:提供了 2D 和 3D 圖形渲染的 API获雕,高GPU的能力,并實(shí)現(xiàn)硬件加速渲染收捣,是第一個(gè)和圖形硬件(GPU)交流的標(biāo)準(zhǔn)化方式
- Core Graphics:Quartz 2D的一個(gè)高級(jí)繪圖引擎届案,常用與iOS,tvOS罢艾,macOS的圖形繪制應(yīng)用開發(fā)楣颠。Core Graphics是對(duì)底層C語言的一個(gè)簡單封裝尽纽,其中提供大量的低層次,輕量級(jí)的2D渲染API童漩。前綴為CG弄贿,比如常見的CGPath,CGColor
- Core Animation:是蘋果提供的一套基于繪圖的動(dòng)畫框架矫膨,但不止是動(dòng)畫差凹,他同樣是繪圖的根本【因?yàn)槠淝熬Y是CA,CALayer有多重要就不用多說了吧】
6379271-f8367726caa19e8a
- 從圖中可以看出侧馅,最底層是圖形硬件(GPU)危尿;上層是OpenGL和CoreGraphics,提供一些接口來訪問GPU施禾;再上層的CoreAnimation在此基礎(chǔ)上封裝了一套動(dòng)畫的API。最上面的UIKit屬于應(yīng)用層搁胆,處理與用戶的交互
- Core Image:iOS處理圖像的框架弥搞,主要用處可以給圖片添加濾鏡效果和圖像識(shí)別功能
we are A!R渠旁!G攀例!B!
- 這里講下對(duì)于像素需要知道的知識(shí):
- 屏幕上的像素是由紅顾腊,綠粤铭,藍(lán)三種顏色組件構(gòu)成的。因此杂靶,位圖數(shù)據(jù)有時(shí)也被叫做 RGB 數(shù)據(jù)
- ARGB:32bits-per-pixel(bpp), 8bits-per-componet(bpc),透明度會(huì)首先被乘以到像素值上【也就是說對(duì)于透明度的處理我們直接就是百分比乘到RGB值里面】
A R G B A R G B A R G B
| pixel 0 | pixel 1 | pixel 2
0 1 2 3 4 5 6 7 8 9 10 11 ...
這個(gè)格式經(jīng)常被叫做 ARGB梆惯。每個(gè)像素占用 4 字節(jié)(32bpp),每一個(gè)顏色組件是1字節(jié)(8bpc).每個(gè)像素有一個(gè) alpha 值,這個(gè)值總是最先得到的(在RGB值之前)吗垮,最終紅垛吗、綠、藍(lán)的值都會(huì)被預(yù)先乘以 alpha 的值
如果我們有一個(gè)橙色烁登,他們各自的 8bpc 就像這樣: 240,99,24.一個(gè)完全不透明的橙色像素?fù)碛械?ARGB 值為: 255怯屉,240,99饵沧,24锨络,它在內(nèi)存中的布局就像上面圖示那樣。如果我們有一個(gè)相同顏色的像素狼牺,但是 alpha 值為 33%羡儿,那么他的像素值便是:84,80是钥,33失受,8.
- xRGB:像素并沒有任何 alpha 值(他們都被假定為100%不透明)讶泰,但是內(nèi)存布局是一樣的,這樣子將會(huì)節(jié)約內(nèi)存【拂到?痪署??:這種格式容易被現(xiàn)代的 CPU 和繪圖算法消化兄旬,因?yàn)槊恳粋€(gè)獨(dú)立的像素都對(duì)齊到 32-bit 的邊界】
x R G B x R G B x R G B
| pixel 0 | pixel 1 | pixel 2
0 1 2 3 4 5 6 7 8 9 10 11 ...
- xRGB即沒有alpha通道的圖片狼犯,它無法選擇透明度,雖然我們我們說有alpha通道的像素點(diǎn)其實(shí)就是將alpha預(yù)乘到了RGB里面领铐,但這不意味著alpha沒用悯森,在合成像素點(diǎn)的時(shí)候我們依然需要用alpha值計(jì)算混合后的值,所以對(duì)于這種圖片就是無法設(shè)置透明度
合成
- 在有多個(gè)圖層重疊的情況下绪撵,屏幕上的人看到的一個(gè)像素點(diǎn)可能是好幾個(gè)像素點(diǎn)合成后的結(jié)果瓢姻,這就需要我們?nèi)ビ?jì)算出現(xiàn)的像素應(yīng)該是什么造型
- 下面是理想狀態(tài)下的像素合成公式:
R = S + D * ( 1 – Sa )
結(jié)果的顏色 = 源色彩(頂端紋理) + 目標(biāo)顏色(低一層的紋理) * (1 - 源顏色的透明度)
- 但理想狀態(tài)很少見,更多情況下音诈,我們需要進(jìn)行更為復(fù)雜的計(jì)算
透明與不透明
- 這里先區(qū)分三個(gè)概念
Alpha幻碱,Hidden與Opaque區(qū)別
- alpha是不透明度,屬性為浮點(diǎn)類型的值细溅,取值范圍從0到1.0褥傍,表示從完全透明到完全不透明,其特性有當(dāng)前UIView的alpha值會(huì)被其所有subview繼承喇聊。alpha值會(huì)影響到UIView跟其所有subview恍风,alpha具有動(dòng)畫效果。當(dāng)alpha為0時(shí)誓篱,跟hidden為YES時(shí)效果一樣朋贬,但是alpha主要用于實(shí)現(xiàn)隱藏的動(dòng)畫效果,在動(dòng)畫塊中將hidden設(shè)置為YES沒有動(dòng)畫效果
- 設(shè)置backgroundColor的alpha值只影響當(dāng)前UIView的背景窜骄,并不會(huì)影響其所有subview兄世。Clear Color就是backgroundColor的alpha為1.0。alpha值會(huì)影響backgroundColor最終的alpha,假設(shè)UIView的alpha為0.8啊研,backgroundColor的alpha為0.5御滩,那么backgroundColor最終的alpha為0.4(0.8*0.5)
- Hidden表示UIView是否隱藏,Hidden設(shè)置為YES表示當(dāng)前UIView的所有subview也會(huì)被隱藏党远,忽略subview的hidden屬性削解。Hidden只要設(shè)置為YES,所有的subview都會(huì)隱藏沟娱。UIView隱藏之后也會(huì)從當(dāng)前的響應(yīng)者事件中移除
- opaque表示當(dāng)前的UIView的是否不透明【BOOL值】氛驮,但它就是涉及到了像素渲染問題,對(duì)于opaque為1的圖層济似,將會(huì)直接顯示矫废,不會(huì)再去計(jì)算合成
合成區(qū)別
- 顯然盏缤,盡量多用opaque 為 YES的圖層可以節(jié)約GPU的消耗,加快渲染時(shí)間
- 如果你加載一個(gè)沒有 alpha 通道的圖片蓖扑,并且將它顯示在 UIImageView 上唉铜,會(huì)自動(dòng)設(shè)置opaque 為 YES
- 一個(gè)圖片沒有 alpha 通道和一個(gè)圖片每個(gè)地方的 alpha 都是100%,這將會(huì)產(chǎn)生很大的不同律杠。在后一種情況下潭流,Core Animation 需要假定是否存在像素的 alpha 值不為100%,也就是說依然需要進(jìn)行運(yùn)算
對(duì)齊與不對(duì)齊
- 如果幾個(gè)圖層的模版都是完美重合柜去,那我們只要從第一個(gè)像素到最后一個(gè)像素都計(jì)算合成一下
- 但是如果像素沒有對(duì)齊好灰嫉,我們還需要額外進(jìn)行額外的移位操作,合并原紋理上的像素
- 兩種情況會(huì)導(dǎo)致不對(duì)齊出現(xiàn):
- 第一個(gè)便是縮放嗓奢;當(dāng)一個(gè)紋理放大縮小的時(shí)候讼撒,紋理的像素便不會(huì)和屏幕的像素排列對(duì)齊
- 另一個(gè)原因便是當(dāng)紋理的起點(diǎn)不在一個(gè)像素的邊界上
離屏渲染(Offscreen Rendering)
- 在屏幕中,我們要顯示的位圖直接存在當(dāng)前屏幕的后備存儲(chǔ)區(qū)股耽,同時(shí)這一塊內(nèi)容將會(huì)很快被刷新到屏幕上呈現(xiàn)
- 但加入我們需要寫一個(gè)簡單的平移動(dòng)畫根盒,假設(shè)有60幀動(dòng)畫,那我們就需要重復(fù)生成60個(gè)圖層豺谈,并應(yīng)用
- 這是因?yàn)槠聊痪瓦@么大郑象,我們放在當(dāng)前屏幕的后備存儲(chǔ)區(qū)必須得是即將放上去的內(nèi)容
- 這樣的操作方式顯然很浪費(fèi)GPU贡这,所以遇到動(dòng)畫Core Animation會(huì)自動(dòng)觸發(fā)離屏渲染
什么是離屏渲染(Offscreen Rendering)
CRT顯示器
CRT顯示器是靠電子束激發(fā)屏幕內(nèi)表面的熒光粉來顯示圖像的茬末,由于熒光粉被點(diǎn)亮后很快會(huì)熄滅,所以電子槍必須循環(huán)地不斷激發(fā)這些點(diǎn)
首先盖矫,在熒光屏上涂滿了按一定方式緊密排列的紅丽惭、綠、藍(lán)三種顏色的熒光粉點(diǎn)或熒光粉條辈双,稱為熒光粉單元责掏,相鄰的紅、綠湃望、藍(lán)熒光粉單元各一個(gè)為一組换衬,學(xué)名稱之為像素。每個(gè)像素中都擁有紅证芭、綠瞳浦、藍(lán)(R、G废士、B)三基色
顯示原理
- 顯示器顯示出來的圖像是經(jīng)過 CRT電子槍一行一行的掃描.(可以是橫向的也可以是縱向),掃描出來就呈現(xiàn)了一幀畫面,隨后電子槍又會(huì)回到初始位置循環(huán)掃描,為了讓顯示器的顯示跟視頻控制器同步,當(dāng)電子槍新掃描一行的時(shí)候.準(zhǔn)備掃描的時(shí)候,會(huì)發(fā)送一個(gè) 水平同步信號(hào)(HSync信號(hào)),而當(dāng)一幀畫面繪制完成后,電子槍回復(fù)到原位叫潦,準(zhǔn)備畫下一幀前,顯示器會(huì)發(fā)出一個(gè)垂直同步信號(hào)(vertical synchronization簡稱 VSync)
- 顯示器一般是固定刷新頻率的,這個(gè)刷新的頻率其實(shí)就是VSync信號(hào)產(chǎn)生的頻率. 然后CPU計(jì)算好frame等屬性,就將計(jì)算好的內(nèi)容提交給GPU去渲染,GPU渲染完成之后就會(huì)放入幀緩沖區(qū),然后視頻控制器會(huì)按照VSync信號(hào)逐行讀取幀緩沖區(qū)的數(shù)據(jù),經(jīng)過可能的數(shù)模轉(zhuǎn)換傳遞給顯示器.就顯示出來了
離屏渲染原理
- 離屏渲染也就是說在屏幕外劃出一片緩存區(qū)作為屏幕外緩存區(qū)官硝,用來存儲(chǔ)需要渲染又不能馬上放入屏幕內(nèi)緩存區(qū)的紋理矗蕊,這塊大小大約有屏幕大小兩倍的空間
- 這種做法的實(shí)質(zhì)就是將CPU拉來做GPU的事短蜕,讓CPU在屏幕外進(jìn)行渲染工作【雖然CPU性能比GPU強(qiáng)得多,但是在渲染這方面還不如GPU傻咖,用殺雞焉用牛刀的感覺】
- 離屏渲染這個(gè)機(jī)制出現(xiàn)的原因在于Apple對(duì)于流暢度要求很高朋魔,寧愿用空間【性能】換時(shí)間
- 但這是一種消耗極大的方式,不僅是CPU渲染有開銷没龙,還包括兩次昂貴的環(huán)境轉(zhuǎn)換(轉(zhuǎn)換環(huán)境到屏幕外緩沖區(qū)铺厨,然后轉(zhuǎn)換環(huán)境到幀緩沖區(qū))
- 這樣的好處就是可以復(fù)用,避免不必要的GPU渲染
- 另外復(fù)用也不是隨意復(fù)用的硬纤,這個(gè)可以被復(fù)用的位圖同樣是只能存在一段時(shí)間解滓,之后會(huì)被卸載掉,你需要計(jì)算GPU 的利用率和幀的速率來判斷這個(gè)位圖是否有用
調(diào)試方式
- 調(diào)試主推薦Instrument里面的Core Animation工具
- 它可以查看項(xiàng)目的fps筝家,GPU占用率
5FAD78E6BB6CC85AD123A7F166D02A18
- 但是我遇到了一個(gè)問題就是這個(gè)工具只能在真機(jī)上用洼裤,在模擬機(jī)上都說該設(shè)備不支持這個(gè)操作,不知道是哪一步整錯(cuò)了
- 另一個(gè)是Xcode -> Debug > View Debugging > Rendering大法
截屏2019-10-15下午8.33.40
- 這個(gè)請(qǐng)注意要在運(yùn)行的時(shí)候才能點(diǎn)開
- Color Blended Layers 出現(xiàn)圖層混合的地方會(huì)標(biāo)注為紅色溪王,沒有圖層混合的地方會(huì)顯示為綠色腮鞍,方向是紅色越少越好,綠色越多越好
- Color Hits Green and Misses Red 當(dāng)使用光柵化渲染(shouldRasterize)的時(shí)候莹菱,如果圖層是綠色移国,表示這些緩存被復(fù)用,如果圖層是紅色表示緩存沒有被復(fù)用會(huì)重復(fù)創(chuàng)建道伟,這時(shí)候會(huì)造成性能問題迹缀。
- 一般這兩個(gè)用的比較多
- 還有一個(gè)Color Offscreen-rendered Yellow 用來檢測(cè)是否離屏渲染【off-screen Rendering】
- GPU的渲染有兩種,On-screen Rendering當(dāng)前屏幕渲染蜜徽,是指GPU的渲染在當(dāng)前屏幕的緩沖區(qū)內(nèi)進(jìn)行祝懂。off-screen Rendering是指在GPU的渲染發(fā)生在當(dāng)前屏幕之外新開辟的緩沖區(qū)。開辟新的緩沖區(qū)拘鞋,切換緩沖區(qū)等會(huì)對(duì)性能有較大的影響
- 同時(shí)砚蓬,有七種情況會(huì)觸發(fā)離屏渲染
- cornerRadius以及masksToBounds同時(shí)使用時(shí)會(huì)觸發(fā)離屏渲染,單獨(dú)使用時(shí)不會(huì)觸發(fā)
- 設(shè)置shadow盆色,而且shodowPath = nil時(shí)會(huì)觸發(fā)
- mask 設(shè)置蒙版會(huì)觸發(fā)
- layer.shouldRasterize的不適當(dāng)使用會(huì)觸發(fā)離屏渲染
- layer.allowsGroupOpacity iOS7以后默認(rèn)開啟灰蛙;當(dāng)layer.opacity != 1.0且有subLayer或者背景圖時(shí)會(huì)觸發(fā)
- layer.allowsEdgeAntialiasing 在iOS8以后的系統(tǒng)里可能已經(jīng)做了優(yōu)化,并不會(huì)觸發(fā)離屏渲染隔躲,不會(huì)對(duì)性能造成影響
- 重寫了drawRect
iOS繪制
CALayer與UIView
<img src="https://tva1.sinaimg.cn/large/006y8mN6ly1g8jqfuqu0wj30eg03paam.jpg" alt="img" style="zoom:200%;" />
- 每一個(gè)UIView都有一個(gè)layer摩梧,每一個(gè)layer都有個(gè)content,這個(gè)content指向的是一塊緩存蹭越,叫做backing store
- 在沒有重寫drawRect方法的情況下障本,CALayer的content為空,重寫后系統(tǒng)會(huì)為該layer的content開辟一塊緩存,大小
size = width * height * scale
驾霜,用來存放drawRect繪制的內(nèi)容【就算重寫內(nèi)容為空也會(huì)新建緩存案训,注意】
代碼驗(yàn)證
//
// MyView.m
// TableView-Optimizing-ChemeDemo
//
// Created by Kevin.J on 2019/11/2.
// Copyright ? 2019 姜?jiǎng)P文. All rights reserved.
//
#import "MyView.h"
@implementation MyView
-(void)drawRect:(CGRect)rect{
NSLog(@"4MyView.before.drawRect.layer.contents %@",self.layer.contents);
// CGContextRef context = UIGraphicsGetCurrentContext();
// draw something
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"3MyView.after.drawRect.layer.contents %@",self.layer.contents);
});
}
@end
//
// ViewController.m
// TableView-Optimizing-ChemeDemo
//
// Created by Kevin.J on 2019/10/8.
// Copyright ? 2019 姜?jiǎng)P文. All rights reserved.
//
#import "ViewController.h"
#import "AsyncLabel.h"
#import "MyView.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
//AsyncLabel *label = [[UILabel alloc] init];
MyView* myView = [[MyView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"1MyView.after.drawRect.layer.contents %@",myView.layer.contents);
});
[self.view addSubview:myView];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"2MyView.after.drawRect.layer.contents %@",myView.layer.contents);
});
}
@end
輸出的順序?yàn)椋?4MyView.before.drawRect.layer.contents (null)
1MyView.after.drawRect.layer.contents <CABackingStore 0x7f9aa8f00ff0>
2MyView.after.drawRect.layer.contents <CABackingStore 0x7f9aa8f00ff0>
3MyView.after.drawRect.layer.contents <CABackingStore 0x7f9aa8f00ff0>
繪制細(xì)節(jié)
- CALayer提供了三種繪制內(nèi)容的方式,若采用優(yōu)先級(jí)高的方式繪制了內(nèi)容粪糙,則低優(yōu)先級(jí)的則不會(huì)執(zhí)行
img
繪制時(shí)機(jī)
- 當(dāng)在操作 UI 時(shí)强霎,比如改變了 Frame、更新了 UIView/CALayer 的層次時(shí)蓉冈,或者手動(dòng)調(diào)用了 UIView/CALayer 的 setNeedsLayout/setNeedsDisplay方法后城舞,這個(gè) UIView/CALayer 就被標(biāo)記為待處理,并被提交到一個(gè)全局的容器去寞酿。
- 蘋果注冊(cè)了一個(gè) Observer 監(jiān)聽 BeforeWaiting(即將進(jìn)入休眠) 和 Exit (即將退出Loop) 事件家夺,回調(diào)去執(zhí)行一個(gè)很長的函數(shù):
- _ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv()。這個(gè)函數(shù)里會(huì)遍歷所有待處理的 UIView/CAlayer 以執(zhí)行實(shí)際的繪制和調(diào)整伐弹,并更新 UI 界面
- 當(dāng)UIView被繪制時(shí)(從 CA::Transaction::commit:以后)拉馋,CPU執(zhí)行drawRect,通過context將數(shù)據(jù)寫入backing store惨好。當(dāng)backing store寫完后煌茴,通過render server交給GPU去渲染,將backing store中的bitmap數(shù)據(jù)顯示在屏幕上
參考文章
- iOS性能優(yōu)化(初級(jí))
- iOS性能優(yōu)化(中級(jí))
- iOS性能優(yōu)化(中級(jí)+): 異步繪制
- iOS繪制與渲染--CPU繪制
- iOS-CPU&&GPU分別做什么日川?
- CoreText是如何繪制文本的
- 【重讀iOS】認(rèn)識(shí)CALayer
- 為什么必須在主線程操作UI
- 異步繪制
- 位圖:百度百科
- 繪制像素到屏幕上
- iOS開發(fā)-Alpha蔓腐,Hidden與Opaque區(qū)別
- [技巧]UIView的hidden和alpha的妙用
- 關(guān)于離屏渲染
- iOS-離屏渲染詳解
- 離屏渲染優(yōu)化詳解:實(shí)例示范+性能測(cè)試
- 淺談iOS中的視圖優(yōu)化