专注iOS开发优化专题iOS技术交流

iOS优化:解决iOS中像素不对齐问题

2017-05-08  本文已影响1345人  南华coder

[这是第1篇]

导语:像素对齐并不是一个复杂的问题,但是开发中稍不注意的话,是会造成像素不对齐的情况(恰恰容易被忽视掉),本文使用一个案例来分析如何解决像素不对齐问题。

背景知识:像素对齐

1、基础
2、像素对齐 VS 像素不对齐
3、发现像素不对齐
优化前.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

上一篇下一篇

猜你喜欢

热点阅读