最近在做GPU圖層顯示的優(yōu)化,其中關(guān)于Color Misaligned Images優(yōu)化文章有很多乐设,但在具體優(yōu)化的時(shí)候還是遇到了點(diǎn)的問題,特此記錄绎巨。
檢測方式
以下兩種方式均可發(fā)現(xiàn)存在Misaligned Images問題的地方:
- 模擬器調(diào)試時(shí)近尚,打開模擬器的Debug - Color Misaligned Images菜單選項(xiàng)。最快捷场勤,但僅限模擬器上查看戈锻。
- Instrument性能檢測時(shí),選中Core Animation模板和媳,在Display Settings中勾選Color Misaligned Images選項(xiàng)格遭。可針對模擬器和真機(jī)留瞳,可查看真機(jī)上所有應(yīng)用的像素混合情況拒迅。
問題定義
打開開關(guān)后,看到部分視圖會有黃色或洋紅色(Magenta)的圖層標(biāo)記,代表其像素不對齊璧微。
不對齊:視圖或圖片的點(diǎn)數(shù)(point)作箍,不能換算成整數(shù)的像素值(pixel),導(dǎo)致顯示視圖的時(shí)候需要對沒對齊的邊緣進(jìn)行額外混合計(jì)算前硫,影響性能胞得。
洋紅色:UIView的frame像素不對齊,即不能換算成整數(shù)像素值屹电。
黃色:UIImageView的圖片像素大小與其frame.size不對齊阶剑,圖片發(fā)生了縮放造成。
優(yōu)化方式
frame像素不對齊
針對frame像素不對齊危号,借助ceilf()牧愁、floorf()、CGRectIntegral()等將小數(shù)點(diǎn)后數(shù)據(jù)除去即可葱色。
使用floorf時(shí)递宅,需要注意是否會因?yàn)橄蛳氯≌绊懸晥D的顯示。
關(guān)于CGRectIntegral的使用苍狰,《Aligned UIViews》這篇文章中提到一種非常特殊的情況办龄,在layoutSubviews中使用CGRectIntegral來重新設(shè)置frame,可能導(dǎo)致同一個(gè)view在不同時(shí)候計(jì)算得到到的x和width不同的情況淋昭,但實(shí)際測試并沒有發(fā)現(xiàn)文章中描述的問題俐填。
0.5個(gè)點(diǎn),會造成像素不對齊嗎翔忽?
在@2x屏幕上不會英融,但@3x屏幕上會。會不會由最終計(jì)算得像素值是不是整數(shù)判斷歇式,比如上圖中在@3x屏幕上驶悟,第4個(gè)label高度為40.1導(dǎo)致了像素不對齊,但第3個(gè)label高度為40+1/3沒導(dǎo)致像素不對齊材失,在@2x屏幕上當(dāng)然這兩個(gè)寬度都會導(dǎo)致像素不對齊痕鳍。
像素不對稱齊的元素一般為UILabel或UIImageView。
特別注意龙巨,上圖中UILabel寬度不為整數(shù)時(shí)并沒有有像素不對齊笼呆,但x、y旨别、height不為整數(shù)就會導(dǎo)致像素不對齊诗赌。
圖片像素不對齊
上圖的前4個(gè)UIImageView,顯示的是同一張圖片秸弛,該圖片@2x像素為128x128px铭若,@3x像素為192x192px洪碳,僅當(dāng)UIImageView的size為64x64的時(shí)候才沒有像素不對齊。
遇到這種情況需要嚴(yán)格約束Icon圖片和UIImageView的尺寸奥喻。
還有種情況即圖片是從服務(wù)端獲取到的偶宫,大小不規(guī)則。直接在UIImageView上顯示容易出現(xiàn)像素不對齊环鲤。
解決方法:將下載到的圖片纯趋,縮放到與UIImageView對應(yīng)的尺寸,再顯示出來冷离。
多種圖片縮放方式及其性能比較可參考《Image Resizing Techniques》吵冒,此處提供一個(gè)簡單實(shí)現(xiàn):
@implementation UIImage(Resize)
/**
將UIImage縮放到指定大小
@param boxSize 一般為UIImageView的size
@return 縮放后的UIImage
*/
- (UIImage *)imageShowInSize:(CGSize)boxSize {
if (CGSizeEqualToSize(boxSize, self.size)) {
return self;
}
CGFloat screenScale = [[UIScreen mainScreen] scale];
CGFloat rate = MAX(boxSize.width / self.size.width, boxSize.height / self.size.height);
CGSize resize = CGSizeMake(self.size.width * rate , self.size.height * rate );
CGRect drawRect = CGRectMake(-(resize.width - boxSize.width) / 2.0 ,
-(resize.height - boxSize.height) / 2.0 ,
resize.width,
resize .height);
boxSize = CGSizeMake(boxSize.width, boxSize.height);
UIGraphicsBeginImageContextWithOptions(boxSize, YES, screenScale);
[self drawInRect:drawRect];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
@end
注意:
- 具體使用時(shí),可能需要根據(jù)UIImageView的contentMode屬性調(diào)整縮放方式西剥。
- 該方法執(zhí)行會花費(fèi)一定的時(shí)間痹栖,在列表上顯示需要縮放的圖片,為了不影響列表滾動流程體驗(yàn)瞭空,該操作應(yīng)放到非主線進(jìn)行揪阿,并考慮將縮放后的結(jié)果緩存以便下次直接使用。
- 根據(jù)原始圖片尺寸大小咆畏,當(dāng)前狀況是否明顯影響列表滾動等具體情況再決定是否優(yōu)化南捂,比如目前微博首頁的用戶頭像和九宮格圖片不存在像素不對齊情況,而微信朋友圈的用戶頭像和圖片是染成黃色的像素不對齊旧找。
UITableview上UILabel的不對齊
在某些UITableview上溺健,會發(fā)現(xiàn)盡管UILabel的frame已經(jīng)取整了,但所有Cell上Label還是全都被染成了紅色钮蛛,非常不解鞭缭。
打開Xcode的Debug View Hierarchy,可以看到進(jìn)入頁面UITableview還沒做任何滾動時(shí)魏颓,UILabel的frame沒有異常岭辣,但是UITableViewCell的y坐標(biāo)不是整數(shù),有個(gè)0.01的差值甸饱。
這次恍然大悟易结,父視圖的像素不對齊也會影響到子視圖。而此處0.01差值的來源柜候,是UITableview的header高度。
在使用Group Style的UITableview時(shí)躏精,如果tableView:heightForHeaderInSection:回調(diào)返回0渣刷,系統(tǒng)會認(rèn)為沒有設(shè)置header的高度而重新提供一個(gè)默認(rèn)的header高度,導(dǎo)致在UITableview中看到一個(gè)空白的header矗烛。
一種簡單但有隱患的處理方式辅柴,就是在回調(diào)里返回一個(gè)很小的高度箩溃,比如0.1、0.01碌嘀,這樣能達(dá)到隱藏header的效果涣旨,但也造成了此處的像素不對齊問題。
解決方法:
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
return CGFLOAT_MIN;
}
避免使用0.01這樣的具體數(shù)值(0.01還不足夠小股冗,0.0001就能避免此處的不對齊)霹陡,直接使用系統(tǒng)給的CGFLOAT_MIN,這個(gè)足夠小的值既能避免上述情況止状,又能讓代碼更直觀烹棉。
隱藏header的其它方法和原理可參考:0代碼隱藏GroupedTableView上邊多余的間隔。
參考文章: