由于项目中有大量的上传图片,之前图片的处理方式直接就用了UIImageJPEGRepresentation方式来处理,也用了很长的一段时间,后面由于权限的开放,从相册选择的使用频率变高,由于图片的来源不一,所以相片的质量不一样,有的是客户从其他渠道传输过来的,肯定也经过了压缩的处理,所以再经过我们这边统一的方法再去压缩的话,将会导致上传的图片失真,不清晰。针对以上痛点,于是有了下面的处理方式.
压缩方式1:
循环压缩法
/// 压缩图片数据-不压尺寸
///
/// - Parameters:
/// - maxLength: 最大长度
/// - Returns:
func compressImageOnlength(maxLength: Int) -> Data? {
guard let vData = UIImageJPEGRepresentation(self, 1) else { return nil }
XYJLog(message: "压缩前kb: \( Double((vData.count)/1024))")
if vData.count < maxLength {
return vData
}
var compress:CGFloat = 0.9
guard var data = UIImageJPEGRepresentation(self, compress) else { return nil }
while data.count > maxLength && compress > 0.01 {
XYJLog(message: "压缩比: \(compress)")
compress -= 0.02
data = UIImageJPEGRepresentation(self, compress)!
}
XYJLog(message: "压缩后kb: \(Double((data.count)/1024))")
return data
}
由于我们的需求是最多可以五张图片拼接处理,最后压缩的时候,采用此方法,可能会导致压缩时间比较久,用户体验不友好,于是采用了方式二
压缩方式2:
二分压缩法
//二分压缩法
func compressImageMid(maxLength: Int) -> Data? {
var compression: CGFloat = 1
guard var data = UIImageJPEGRepresentation(self, 1) else { return nil }
XYJLog(message: "压缩前kb: \( Double((data.count)/1024))")
if data.count < maxLength {
return data
}
print("压缩前kb", data.count / 1024, "KB")
var max: CGFloat = 1
var min: CGFloat = 0
for _ in 0..<6 {
compression = (max + min) / 2
data = UIImageJPEGRepresentation(self, compression)!
if CGFloat(data.count) < CGFloat(maxLength) * 0.9 {
min = compression
} else if data.count > maxLength {
max = compression
} else {
break
}
}
var resultImage: UIImage = UIImage(data: data)!
if data.count < maxLength {
return data
}
方式一、二调用的方式如下
// 压缩到1M 以内
var imageData = uploadsizeImg?.compressImageMid(maxLength: 1024*1024)
采用二分压缩法在效率上有一定的提升,但是对于五张图片合成后,如果尺寸越大,越越容易导致内存出现问题,严重的将会导致崩溃,所以在压缩数据前我们需要对合成的图片做尺寸上的压缩
尺寸压缩法
/// 根据尺寸重新生成图片
///
/// - Parameter size: 设置的大小
/// - Returns: 新图
public func imageWithNewSize(size: CGSize) -> UIImage? {
if self.size.height > size.height {
let width = size.height / self.size.height * self.size.width
let newImgSize = CGSize(width: width, height: size.height)
UIGraphicsBeginImageContext(newImgSize)
self.draw(in: CGRect(x: 0, y: 0, width: newImgSize.width, height: newImgSize.height))
let theImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
guard let newImg = theImage else { return nil}
return newImg
} else {
let newImgSize = CGSize(width: size.width, height: size.height)
UIGraphicsBeginImageContext(newImgSize)
self.draw(in: CGRect(x: 0, y: 0, width: newImgSize.width, height: newImgSize.height))
let theImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
guard let newImg = theImage else { return nil}
return newImg
}
}
图片合成问题补充
1. 合成图片:由于上传确认页面的图片可以点击进去编辑图片,或者添加图片,如果这里每添加一张图片或者合成一张图片的话,会相当的耗费性能,所以合成图片的操作放在点击上传按钮那里
点击上传调用合成图片方法,合成图片方法主要分两步,实现思路如下:
(1)创建一个contentView用来装多个ImageView,根据图片数组的数量for循环来添加imageview,imageview设置图片的时候先对图片进行数据压缩处理,然后再对尺寸进行压缩处理,下面是压缩处理规则:
if imagedata.count < 0.8M {
不压缩
} else if imagedata.count > 0.8M && imagedata.count <1M {
压缩系数0.8获取图像
} else if imagedata.count > 1M && imagedata.count <2M {
压缩系数0.3获取图像
} else {
压缩系数0.1获取图像
}
压缩完数据以后再进行尺寸压缩
if 图片宽度>两倍屏幕宽 || 图片高度>两倍屏幕高 {
图片大小缩放系数0.5处理
}
缩放之后设置到imageView上
(2)通过for循环来获取contentView上的imageView上的图片,根据已经处理好的image来设置imageview的frame,排好版面到contentView上,最后就是将contentView转化成image,并且赋值给合成图片变量composeImage
以上两个for循环中的内容置于autoreleasepool中
整个上传流程
点击上传 ->进入合成图片方法,显示正在加载的圈圈,延时1.5秒进入上传方法 -> 二分法压缩图片数据到1M以内,图片大于3张延时3秒,否则2秒,延时之后执行隐藏压缩圈圈方法,发起正式上传网络请求 -> 上传成功之后,图片变量置为nil,移除图片数组元素,返回上一级,查看deinit打印执行
测试与分析
6测试---这里还是之前的合成图片后,再对合成图片压缩尺寸
如果一进入上传页面页面显示且绘制合成图片51 -> 61 大概增加10M到150M
五张合成后再缩放图片:大概增加100 到150M
单独测试压缩方法 增加大概100M
单独测试压缩方法加上传 增加大概110M
缩放图片尺寸加压缩 测试大概增加150M
缩放 压缩 上传一气呵成大概也是增加150M左右
综上最耗内存的两个方法 一个是缩放尺寸(会进行绘制) 一个是压缩
大小手机测试:
5s 上传7+拍的5张照片 整个上传过程 最高出现增加350M
5s 上传自己拍的5张 最高出现增加240M
分析:
1.在几个for循环里面加个自动释放池感觉没有缓解什么,几M的区别
2.缩放的的图片被压缩成data后,马上置为nil 整个上传过程增加100-130 所以这里有一定的作用
3. 有的图片的长宽高达屏幕的5,6倍,所以在合成之前,我们可以把每一张图片缩放一半,后面整张合成的图片将不再进行缩放,经测试这样合成图片,再去上传图片,我用5s上传7+拍的图片大概增加120M,相比以前的240,350节约一大半的内存,可以有效防止内存不足闪退。
总结:经过以上的方法处理,图片的质量和内存有了很大的改善,值得注意的是,我们在处理图片的时候,一定要注意测试内存,如果内存没有释放,将随着拍摄次数的增多,内存逐渐增大,最后的结果就是崩溃,可能客户跟你反馈,你还会一脸懵逼,当然知道这个问题就好办了,这种情况一般是图片没有释放,或者代码里有循环引用所致,解决好就可以了。
网友评论