iOS優化:解決iOS中像素不對齊問題

[這是第1篇]

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

背景知識:像素對齊

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

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

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

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

  • 出現像素不對齊的情況,會導致在GPU渲染時,對沒對齊的邊緣,需要進行插值計算,這個插值計算的過程會有性能損耗

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

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

  • 因為項目中大量使用UITableView來構建UI界面【詳細參考iOS實錄1:使用UITableView構建UI界面】。下面就QSUseTableViewDemo中的詳情頁來對比優化前后的效果。優化前,開啟模擬器上的Debug -->Color Misaligned Images選項,發現:文本部分出現洋紅色(frame像素不對齊)和 圖片部分是黃色(圖片的縮放導致的不對齊)。

優化前.png

一、文本計算的坑

1、存在的問題

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

2、解決辦法

我們在項目擴展了NSString方法,使用新增的方法統一計算文本的大小,在這些方法中使用ceil()將小數點后數據除去,使得計算的結果小數點后都是0

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

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

/**
 根據字體、行數、行間距和constrainedWidth計算多行文本占據的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 不限制行數
    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、存在的問題

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

2、解決辦法

使用盡可能下的數值,0.01還不夠小,直接使用系統提供的CGFLOAT_MIN吧。

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

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

注意:在設置UITableViewCell的高度時候,使用的浮點數,小數點后不可以有0的數,否則造成像素不對齊。

3、解決效果

注明:經過第一和第二步的優化,文本像素不對齊的問題解決了。

文本像素不對齊解決.png

三、圖片像素不對齊的情況

1、存在的問題

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

2、解決辦法

圖片分為兩種,本地圖片和網絡上下載的圖片,前者是UI提供的,存在項目中,這就要求**UI設計師同事提供@2x和@3x圖片,因為@2x的圖片在@3x的屏幕上也會發生像素不對齊的問題;而網絡上獲取的圖片沒有@2x和@3x的區別,需要我們縮放圖片到與UIImageView對應的尺寸,且縮放后的圖片的scale和[UIScreen mainScreen].scale相等,再顯示出來。

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

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

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

    //創建上下文
    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;
        });
    });

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

3、解決效果

經過第三步的優化,圖片不對齊的問題(黃色區域沒有了)被解決。

圖片像素不對齊解決.png

總結:解決像素不對齊的基本準則

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

源代碼直通車QSUseTableViewDemo

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容