前段時(shí)間項(xiàng)目中有個(gè)特殊的需求烁挟,折騰了好久破衔,覺得有必要總結(jié)一下辟躏。我自己寫了一個(gè)日歷控件若债,產(chǎn)品要求選中的日期要用圓形圖片顯示出來乔妈,如圖所示
方案一
剛開始以為只要用layer.cornerRadius就可以了序目,但是真正實(shí)現(xiàn)起來的時(shí)候才發(fā)現(xiàn)不是那么簡(jiǎn)單犀斋。因?yàn)槲业娜諝v控件寬度設(shè)置為屏幕寬度奏篙,那么問題就來了润努,日歷一行只能顯示七天关斜,所以不同屏幕寬度下每個(gè)單元view的寬度就不固定了,而且還不是正方形铺浇,用layer.cornerRadius的話就會(huì)出來一個(gè)橢圓形的選中效果痢畜,第一種方案就放棄了。
方案二
第二種方案我是想著放一個(gè)遮罩,既然itemView不一定是正方形丁稀,那我們想辦法讓它只顯示出來正方形的區(qū)域就好了吼拥。于是用UIBezierPath+CAShapeLayer畫出一個(gè)圓形的遮罩添加在每個(gè)單元view的layer上,設(shè)置為itemView.layer.mask為畫出的圓形CAShapeLayer线衫,運(yùn)行一看滿足了要求凿可。但出現(xiàn)一個(gè)新的問題就是,每次進(jìn)到這個(gè)界面的時(shí)候屏幕都會(huì)卡頓授账,能看出明顯的抖動(dòng)枯跑,這肯定是不能忍的。只能查找原因優(yōu)化~
大概原因是設(shè)置layer的mask屬性會(huì)觸發(fā)離屏渲染白热,大大的消耗了性能敛助,所以卡頓。其中有一種優(yōu)化方案就是開啟GPU的柵格化棘捣,然后把需要渲染的畫面緩存起來,等下次進(jìn)來的時(shí)候可以直接加載
self.layer.shouldRasterize = YES;
self.layer.rasterizationScale = self.layer.contentsScale;
試了一下果然不卡了休建,但是新的問題又出現(xiàn)了乍恐,就是日歷上的數(shù)字都變得模糊了。我推斷出大概是以下原因测砂,當(dāng)shouldRasterize設(shè)成true時(shí)茵烈,layer被渲染成一個(gè)bitmap,并緩存起來砌些,等下次使用時(shí)不會(huì)再重新去渲染了呜投。實(shí)現(xiàn)圓角本身就是在做顏色混合(blending),如果每次頁(yè)面出來時(shí)都blending存璃,消耗太大仑荐,這時(shí)shouldRasterize = yes,下次就只是簡(jiǎn)單的從渲染引擎的cache里讀取那張bitmap纵东,節(jié)約系統(tǒng)資源粘招。我這邊要渲染的是文字,估計(jì)在layer被渲染成bitmap的過程或者讀取bitmap的時(shí)候產(chǎn)生了一些誤差偎球,所以導(dǎo)致文字看上去有些模糊洒扎。
接著優(yōu)化~
方案三
我在itemView上邊覆蓋一個(gè)中間為圓形透明區(qū)域的圖片,正方形的圖片中心點(diǎn)與itemView的中心點(diǎn)重合衰絮。這樣解決了卡頓問題袍冷,而且也很清晰,但是新的問題出現(xiàn)了猫牡,就是我選中的時(shí)候要改變itemView的顏色胡诗,這時(shí)候如果itemView寬度大于高度的時(shí)候,就會(huì)出現(xiàn)左右兩邊的顏色沒有被覆蓋的情況,沒辦法……
接著優(yōu)化~
方案四
反向選取區(qū)域乃戈,既然處理不了中間的圓形區(qū)域褂痰,那我能不能把周圍的處理一下嘞~最后終于是功夫不負(fù)有心人,就是這個(gè)方法bezierPathByReversingPath症虑,用代碼說話吧
- (void)drawRect:(CGRect)rect{
[super drawRect:rect];
CGContextRef context = UIGraphicsGetCurrentContext();
CGFloat radius = MIN(self.width, self.height)*0.5-2;
UIBezierPath *path = [UIBezierPath bezierPathWithRect:self.bounds];
UIBezierPath *clipPath = [[UIBezierPath bezierPathWithRoundedRect:CGRectMake((self.width-radius*2)*0.5, (self.height-radius*2)*0.5, radius*2, radius*2) cornerRadius:radius] bezierPathByReversingPath];
[path appendPath:clipPath];
CGContextAddPath(context, path.CGPath);
CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor);
CGContextFillPath(context);
}
我自定義了一個(gè)繼承自UILabel的GRCalendarTileLabel控件缩歪,在drawRect方法中把中間圓形區(qū)域以外的區(qū)域設(shè)置為了白色,這樣一來不管你外邊是什么形狀谍憔,我只留出中間一塊兒圓形區(qū)域匪蝙,這里記得調(diào)用[super drawRect:rect]方法,不然給Label設(shè)置text的時(shí)候是不會(huì)顯示的习贫。如下圖所示
這樣一來逛球,問題就算完美解決了。雖然過程中費(fèi)了不少周折苫昌,但是問題解決的時(shí)候還是很有成就感滴颤绕,特此記錄總結(jié)一下,希望能給有同樣需求的小伙伴一點(diǎn)幫助祟身,歡迎大家積極提出指導(dǎo)意見~~~