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像素不对齐)和 图片部分是黄色(图片的缩放导致的不对齐)。

iOS优化:解决iOS中像素不对齐问题_第1张图片
优化前.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、解决效果

注明:经过第一和第二步的优化,文本像素不对齐的问题解决了。

iOS优化:解决iOS中像素不对齐问题_第2张图片
文本像素不对齐解决.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、解决效果

经过第三步的优化,图片不对齐的问题(黄色区域没有了)被解决。

iOS优化:解决iOS中像素不对齐问题_第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

你可能感兴趣的:(iOS优化:解决iOS中像素不对齐问题)