iOS優(yōu)化:解決iOS中像素不對(duì)齊問題

[這是第1篇]

導(dǎo)語:像素對(duì)齊并不是一個(gè)復(fù)雜的問題,但是開發(fā)中稍不注意的話,是會(huì)造成像素不對(duì)齊的情況(恰恰容易被忽視掉),本文使用一個(gè)案例來分析如何解決像素不對(duì)齊問題。

背景知識(shí):像素對(duì)齊

1、基礎(chǔ)
  • iOS設(shè)備上,有邏輯像素(point)和 物理像素(pixel)之分,像素對(duì)齊指的是物理像素對(duì)齊,對(duì)齊就是像素點(diǎn)的值是整數(shù),如某視圖的寬高是100pixel * 100 pixel。

  • point和pixel的比例是通過[[UIScreen mainScreen] scale]來制定的。在沒有視網(wǎng)膜屏之前,1point = 1pixel;但是2x和3x的視網(wǎng)膜屏出來之后,1point等于2pixel或3pixel。

  • 在UI設(shè)計(jì)師提供的設(shè)計(jì)稿標(biāo)注,和在代碼中設(shè)置frame,其中x,y,width,height的單位是 邏輯像素(point);GPU在渲染圖形之前,系統(tǒng)會(huì)將邏輯像素(point)換算成 物理像素(pixel)。

2、像素對(duì)齊 VS 像素不對(duì)齊
  • 邏輯像素(point)乘以2(2x的視網(wǎng)膜屏) 或3(3x的視網(wǎng)膜屏)得到整數(shù)值,或者說得到的浮點(diǎn)數(shù)且小數(shù)點(diǎn)后都是0的,這就像素對(duì)齊了,否則就是像素不對(duì)齊。

  • 出現(xiàn)像素不對(duì)齊的情況,會(huì)導(dǎo)致在GPU渲染時(shí),對(duì)沒對(duì)齊的邊緣,需要進(jìn)行插值計(jì)算,這個(gè)插值計(jì)算的過程會(huì)有性能損耗。

3、發(fā)現(xiàn)像素不對(duì)齊
  • 在模擬器上提供了Debug -->Color Misaligned Images選項(xiàng)可以把像素不對(duì)齊的部分顯示出來;也可以使用Core AnimationDisplay Settings中的Color Misaligned Images選項(xiàng)將像素不對(duì)齊的部分顯示出來

  • 當(dāng)UIView(及其子類)的frame像素不對(duì)齊顯示洋紅色;當(dāng)圖片的像素大小與控件的大小不一致而導(dǎo)致需要縮放時(shí),顯示黃色

  • 因?yàn)轫?xiàng)目中大量使用UITableView來構(gòu)建UI界面【詳細(xì)參考iOS實(shí)錄1:使用UITableView構(gòu)建UI界面】。下面就QSUseTableViewDemo中的詳情頁來對(duì)比優(yōu)化前后的效果。優(yōu)化前,開啟模擬器上的Debug -->Color Misaligned Images選項(xiàng),發(fā)現(xiàn):文本部分出現(xiàn)洋紅色(frame像素不對(duì)齊)和 圖片部分是黃色(圖片的縮放導(dǎo)致的不對(duì)齊)。

優(yōu)化前.png

一、文本計(jì)算的坑

1、存在的問題

理論上設(shè)置View的大小,最好預(yù)先設(shè)置好,盡量不要計(jì)算。但是項(xiàng)目中,很多時(shí)候需要先計(jì)算出文本在某字體下的寬高,再設(shè)置view的frame。,有時(shí)候文本計(jì)算得到的width和height是小數(shù),如16.48、15.32。如果直接使用,必然會(huì)造成像素不對(duì)齊的問題(因?yàn)?6.48、15.32乘以2或3得到的都不是整數(shù))。

2、解決辦法

我們?cè)陧?xiàng)目擴(kuò)展了NSString方法,使用新增的方法統(tǒng)一計(jì)算文本的大小,在這些方法中使用ceil()將小數(shù)點(diǎn)后數(shù)據(jù)除去,使得計(jì)算的結(jié)果小數(shù)點(diǎn)后都是0

//單行的
- (CGSize)textSizeWithFont:(UIFont*)font{

   CGSize textSize = [self sizeWithAttributes:@{NSFontAttributeName:font}];
   textSize = CGSizeMake((int)ceil(textSize.width), (int)ceil(textSize.height));
   return textSize;
}

/**
 根據(jù)字體、行數(shù)、行間距和constrainedWidth計(jì)算多行文本占據(jù)的size
 **/
- (CGSize)textSizeWithFont:(UIFont*)font
                numberOfLines:(NSInteger)numberOfLines
                  lineSpacing:(CGFloat)lineSpacing
             constrainedWidth:(CGFloat)constrainedWidth
            isLimitedToLines:(BOOL *)isLimitedToLines{

    if (self.length == 0) {
        return CGSizeZero;
    }
    CGFloat oneLineHeight = font.lineHeight;
    CGSize textSize = [self boundingRectWithSize:CGSizeMake(constrainedWidth, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:font} context:nil].size;

    CGFloat rows = textSize.height / oneLineHeight;
    CGFloat realHeight = oneLineHeight;
    // 0 不限制行數(shù)
    if (numberOfLines == 0) {
        if (rows >= 1) {
            realHeight = (rows * oneLineHeight) + (rows - 1) * lineSpacing;
        }
    }else{
        if (rows > numberOfLines) {
            rows = numberOfLines;
            if (isLimitedToLines) {
                *isLimitedToLines = YES;  //被限制
            }
        }
        realHeight = (rows * oneLineHeight) + (rows - 1) * lineSpacing;
    }

    return CGSizeMake(ceil(constrainedWidth),ceil(realHeight));
}

@end

二、UITableview的header和footer高度的坑

1、存在的問題

項(xiàng)目中使用Group Style的UITableview,為了避免讓系統(tǒng)去設(shè)置header或者footer的高度,我們自己去設(shè)置tableView:heightForHeaderInSection: tableView:heightForFooterInSection的值,早前做法是直接將其返回0.01f,達(dá)到隱藏header和footer的效果,但是這么做是會(huì)造成像素不對(duì)齊

2、解決辦法

使用盡可能下的數(shù)值,0.01還不夠小,直接使用系統(tǒng)提供的CGFLOAT_MIN吧。

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section{
    return CGFLOAT_MIN;
}

- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section{
    return CGFLOAT_MIN;
}

注意:在設(shè)置UITableViewCell的高度時(shí)候,使用的浮點(diǎn)數(shù),小數(shù)點(diǎn)后不可以有0的數(shù),否則造成像素不對(duì)齊。

3、解決效果

注明:經(jīng)過第一和第二步的優(yōu)化,文本像素不對(duì)齊的問題解決了。

文本像素不對(duì)齊解決.png

三、圖片像素不對(duì)齊的情況

1、存在的問題

圖片的size和顯示圖片的imageView的size(邏輯像素(point))不相等。

2、解決辦法

圖片分為兩種,本地圖片和網(wǎng)絡(luò)上下載的圖片,前者是UI提供的,存在項(xiàng)目中,這就要求**UI設(shè)計(jì)師同事提供@2x和@3x圖片,因?yàn)锧2x的圖片在@3x的屏幕上也會(huì)發(fā)生像素不對(duì)齊的問題;而網(wǎng)絡(luò)上獲取的圖片沒有@2x和@3x的區(qū)別,需要我們縮放圖片到與UIImageView對(duì)應(yīng)的尺寸,且縮放后的圖片的scale和[UIScreen mainScreen].scale相等,再顯示出來。

1)圖片縮放的方法(分類新增UIImage的縮放方法)

- (UIImage *)scaleImageWithSize:(CGSize)size{

    if (CGSizeEqualToSize(size, self.size)) {
        return self;
    }

    //創(chuàng)建上下文
    UIGraphicsBeginImageContextWithOptions(size, YES, [UIScreen mainScreen].scale);

    //繪圖
    [self drawInRect:CGRectMake(0, 0, size.width, size.height)];

    //獲取新圖片
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();  
   return newImage;
}

2)圖片縮放在非主線程,更新圖片在主線程

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        //壓縮背景圖片 & 頭像圖片
        UIImage *bgImage = [[UIImage imageNamed:self.cellModel.bgImageName] scaleImageWithSize:_bgImageView.frame.size];
        UIImage *image = [[UIImage imageNamed:self.cellModel.iconImageName] scaleImageWithSize:_iconImageView.frame.size];
        dispatch_sync(dispatch_get_main_queue(), ^{
            _bgImageView.image = bgImage;
            _iconImageView.image = image;
            _iconImageView.hidden = (image != nil) ? NO : YES;
        });
    });

注明:圖片的縮放是相對(duì)耗時(shí)的,不應(yīng)該放在UI線程(主線程),否則影響UI的流程體驗(yàn);這里使用加載本地圖片,模擬從網(wǎng)絡(luò)獲取圖片。一般項(xiàng)目中使用SDWebImage來下載網(wǎng)絡(luò)圖片,為了更好處理圖片的縮放和圓角等問題,需要在原來庫增加某些特性(圖片縮放、裁圓角和緩存等),這個(gè)后面再說。

3、解決效果

經(jīng)過第三步的優(yōu)化,圖片不對(duì)齊的問題(黃色區(qū)域沒有了)被解決。

圖片像素不對(duì)齊解決.png

總結(jié):解決像素不對(duì)齊的基本準(zhǔn)則

1、frame設(shè)置時(shí)候,使用整數(shù); 需要計(jì)算frame時(shí)候,計(jì)算的結(jié)果使用ceil處理一下,避免小數(shù)點(diǎn)后有非0數(shù)存在。UITableViewCell的高度的高度是整數(shù)。
2、項(xiàng)目中,要求UI設(shè)計(jì)師提供@2x和@3x的切圖。
3、設(shè)置imageView的size要和切圖的size(邏輯像素(point))相等。
4、網(wǎng)絡(luò)上獲取的圖片的size要縮放和imageView的size(邏輯像素(point))要相等,縮放后的圖片的scale和[UIScreen mainScreen].scale要相等。解決方案參考iOS實(shí)錄17:網(wǎng)絡(luò)圖片的優(yōu)化顯示
5、縮放這樣的耗時(shí)操作應(yīng)該放到子線程去做。最好做緩存,避免每次顯示都需要縮放操作。

源代碼直通車QSUseTableViewDemo

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容