阶段工作技术总结
年后来北京这段时间一直在家中办公,目前做的需求主要还是和服务器打交道,取到了数据还要解决与项目里之前的数据冲突,我一直想吐槽二次请求数据的需求,这完全可以让服务器去解决,我第一次拿到的数据就应该是最新的。现实所迫,还是初步提交了相关代码,也总结一下相关的小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处理后,再将这些特殊字符特殊转义,保留其语义。
图片压缩
压
“压” 是指文件体积变小,但是像素数不变,长宽尺寸不变,那么质量可能下降。
对于“压”的功能,我们一般是使用系统提供的UIImageJPEGRepresentation
或UIImagePNGRepresentation
方法实现。
// 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;
}
#####