iOS

阶段工作技术总结

Posted by Renchao on February 20, 2020

阶段工作技术总结

年后来北京这段时间一直在家中办公,目前做的需求主要还是和服务器打交道,取到了数据还要解决与项目里之前的数据冲突,我一直想吐槽二次请求数据的需求,这完全可以让服务器去解决,我第一次拿到的数据就应该是最新的。现实所迫,还是初步提交了相关代码,也总结一下相关的小tip。

封装控件

封装了有项目风格的 底部弹窗选择器日期选择器

封装的中心。

封装一个控件很简单,但是封装的中心是复用,这个控件完美符合我的这个需求,但是明天同事想复用它,却不能很好满足。所以需要思考的是,如何把控件做得饱满,各方面都考虑到。比如我首次封装的底部弹窗选择器,点击任意选项都会关闭这个选择器,但是如果需求是选择完需要请求网络来判断如果不可行,是保留当前选择器,那我这个控件就不符合要求了。因此交互上的逻辑需要缜密思考,做到尽可能全。

学习系统控件的思想。

还是底部弹窗选择器,该如何加入这些不同的选项?系统控件UIAlertController通过添加UIAlertAction,再聚合到controller里,最后present controller。这个思想非常清晰,每个action去管理自己要干的事情。于是我通过添加item,每个item可以去设置标题、颜色、选中图片、点击时间等各种属性。

上传图片遇到的数据不正确问题

一个需求是要求上传image的base64数据,但是服务器返回的一直是数据错误。联调很久,以为是请求的Content-Type不正确,但发现就是服务器要求的application/x-www-form-urlencoded; charset=utf-8。最后看项目里之前关于图片上传的代码,发现imageData经过base64之后,还要进行下面的处理:

NSData *imageData = [self compressionImage:image];
NSString *imageStr = [NSString stringWithFormat:@"%@%@", imageStr, [imageData base64EncodedStringWithOptions:0]];

imageStr = (__bridge_transfer NSString *) CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)imageStr, NULL,  CFSTR(":/?#[]@!$&’()*+,;="), kCFStringEncodingUTF8);

原来是因为数据里本来可能就带有特殊字符,如“?%&”等,这些在URL语法里是有特殊含义的。因此需要base64处理后,再将这些特殊字符特殊转义,保留其语义。

图片压缩

” 是指文件体积变小,但是像素数不变,长宽尺寸不变,那么质量可能下降。

对于“压”的功能,我们一般是使用系统提供的UIImageJPEGRepresentationUIImagePNGRepresentation方法实现。

// return image as PNG. May return nil if image has no CGImageRef or invalid bitmap format
UIKIT_EXTERN NSData *UIImagePNGRepresentation(UIImage *image); 

// return image as JPEG. May return nil if image has no CGImageRef or invalid bitmap format. compression is 0(most)..1(least)                              
UIKIT_EXTERN NSData *UIImageJPEGRepresentation(UIImage *image, CGFloat compressionQuality); 

//UIImageJPEGRepresentation需要传两个参数,
//第一个参数是图片对象
//第二个参数是压的系数,其值范围为0~1
NSData *imgData=UIImageJPEGRepresentation(image, 0.5);

//UIImagePNGRepresentation只需要传一个参数,就是图片对象
NSData *imgData = UIImagePNGRepresentation(image);

UIImagePNGRepresentation要比UIImageJPEGRepresentation(UIImage* image, 1.0)返回的图片数据量大很多。同样的一张照片, 使用前者返回的数据量大小为199K,而后者返回的数据量大小只为140K,比前者少了59K。

如果对图片的清晰度要求不是极高,建议使用UIImageJPEGRepresentation,可以大幅度降低图片数据量。它提供了一个压缩比率的参数compressionQuality,但是实际体验确实compressionQuality并不能够按照设定好的数值,比例压缩。

UIImagePNGRepresentation虽然可以让我们控制压缩质量比例,但是我们看到这个压缩比compressionQuality实际上很难确定一张图片是否能压缩到误差范围内,无法实现精确压缩。

” 是指文件的尺寸变小,也就是像素数减少,而长宽尺寸变小,文件体积同样会减小。

UIImagePNGRepresentation虽然可以让我们控制压缩质量比例,但是我们看到这个压缩比compressionQuality实际上很难确定一张图片是否能压缩到误差范围内,无法实现精确压缩。所以我们对图片只“压”而不缩,有时候是达不到我们的需求的。因此,必要的时候,我们需要适当地对图片“缩”一“缩“尺寸,就可以满足我们的需求。

因此图片压缩最终代码如下:

- (NSData *)compressionImage:(UIImage *)image {
    //设置最大限制为50kb
    NSUInteger maxDatalength = 50 * 1024;
    CGFloat compression = 1.0f;
    NSData *imageData = UIImageJPEGRepresentation(image, compression);
    if (imageData.length >= maxDatalength) {
        CGFloat max = 1;
        CGFloat min = 0;
        //二分法获取压缩系数
        for (int i = 0; i < 6; ++i) {
            compression = (max + min) / 2;
            imageData = UIImageJPEGRepresentation(image, compression);
            if (imageData.length < maxDatalength * 0.9) {
                min = compression;
            } else if (imageData.length > maxDatalength) {
                max = compression;
            } else {
                break;
            }
        }
    }
    //如果此时还是达不到要求,需要缩一缩尺寸
    if (imageData.length >= maxDatalength) {
        UIImage *resultImage = [UIImage imageWithData:imageData];
        NSUInteger lastDataLength = 0;
        while (imageData.length >= maxDatalength && imageData.length != lastDataLength) {
            lastDataLength = imageData.length;
            CGFloat ratio = (CGFloat)maxDatalength / imageData.length;
            CGSize size = CGSizeMake((NSUInteger)(resultImage.size.width * sqrtf(ratio)),
                                     (NSUInteger)(resultImage.size.height * sqrtf(ratio)));
            UIGraphicsBeginImageContext(size);
            [resultImage drawInRect:CGRectMake(0, 0, size.width, size.height)];
            resultImage = UIGraphicsGetImageFromCurrentImageContext();
            UIGraphicsEndImageContext();
            imageData = UIImageJPEGRepresentation(resultImage, 1.0);
        }
    }
    return imageData;
}
判断图片格式
- (NSString *)typeForImageData:(NSData *)data {
    uint8_t c;
    [data getBytes:&c length:1];
    switch (c)
    {
        case 0xFF:
            return @"image/jpeg";
        case 0x89:
            return @"image/png";
        case 0x47:
            return @"image/gif";
        case 0x49:
        case 0x4D:
            return @"image/tiff";
    }
    return nil;
}

#####