现在有个需求,需要截取UIScrollView中内容
主要有以下三种方案
- 基于UIScreenService 实现屏幕截图,系统要求iOS 13以上
- 使用UIGraphicsGetCurrentContext实现或者CoreGraphics 来实现截屏功能
- 分屏截取UIScrollView中的内容, 然后把多张图片拼接成一张图片
问题
-
- 传入view,然后生成PDF data, 系统版本有限制,如果要支持iOS 13一下的不适用
-
- 如果UIScrollView中内容太多,会占用太多的内存空间,导致系统崩溃
-
- 自研方案,内存占用小,没有系统限制
方案3介绍
-
- 保存UIScrollView中默认contentsize,contentoffset
-
- 校验UIScrollView中contentsize属性,如果是竖向滑动,重置height属性,为bounds中height属性的整数倍,为了UIScrollView能够按照设置的contentoffset来滚动
-
- 每次滚动屏幕的整数倍,直到超过最大的contentoffset,每次滚动后就获取当前区域的快照
-
- 将截取的多张图片,绘制成一张图片
-
- 重置contentsize,contentoffset属性
代码如下
- (UIImage * __nullable)netexcept_snapshotImageForScrollview{
@autoreleasepool {
CGPoint saveContentoffset = self.contentOffset;
NSMutableArray *images = [NSMutableArray array];
CGFloat realContentsizeHeight = self.contentSize.height;
//调整contentsize为视图显示高度的整数倍,并保证scrollview按contentoffset设置的参数滚动
CGFloat resizecontentsizeheight = [self adjustTheNumberA: realContentsizeHeight numberB: self.bounds.size.height];
//重新设置contentsize
self.contentSize = CGSizeMake(self.contentSize.width, resizecontentsizeheight);
//获取最大滚动区域
CGFloat maxcontentoffset = resizecontentsizeheight - self.bounds.size.height;
CGPoint coffset = CGPointZero;
do{
[self setContentOffset: coffset];
CGFloat cropHeight = MIN(realContentsizeHeight-coffset.y, self.bounds.size.height);
UIImage *screenshot = [self netexcept_screenshotForCroppingRect:CGRectMake(0, coffset.y, self.contentSize.width, cropHeight)];
if(screenshot && !(screenshot.size.width <= 0 || screenshot.size.height <= 0 || isnan(screenshot.size.width) || isnan(screenshot.size.height))){
[images addObject: screenshot];
}
coffset.y += self.bounds.size.height;
}while (maxcontentoffset >= coffset.y);
UIImage *image = nil;
if(images.count > 0){
image = [UIImage netexcept_verticalImageFromArray: images];
}
self.contentSize = CGSizeMake(self.contentSize.width, realContentsizeHeight);
self.contentOffset = saveContentoffset;
return image;
}
}
//将numberA调整为numberB的整数倍,要求调整后不小于numberA
- (CGFloat)adjustTheNumberA: (CGFloat)numberA numberB: (CGFloat)numberB{
//判断numberA能否被numberB整除,能整除就直接返回
if(numberA/numberB - floor(numberA/numberB) < 0.0000001){
return numberA;
}
//不能整除,就构造一个新的数
CGFloat numberC = 0;
while (numberC < numberA) {
numberC += numberB;
}
return numberC;
}
# 截屏相关
- (UIImage *)netexcept_screenshotForCroppingRect:(CGRect)croppingRect
{
return [self netexcept_screenshotForCroppingRect:croppingRect quantityScale:0.4 sizeScale:0.8];
}
- (UIImage *)netexcept_screenshotForCroppingRect:(CGRect)croppingRect quantityScale: (CGFloat)quantityScale sizeScale: (CGFloat)scale
{
CGFloat sizeScale = quantityScale;
UIGraphicsBeginImageContextWithOptions(CGSizeMake(croppingRect.size.width*sizeScale, croppingRect.size.height*sizeScale), NO, 0.0);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextScaleCTM(context, sizeScale, sizeScale);
if (context == NULL) return nil;
CGContextTranslateCTM(context, -croppingRect.origin.x, -croppingRect.origin.y);
[self layoutIfNeeded];
[self.layer renderInContext:context];
UIImage *screenshotImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
if(screenshotImage){
NSData *imageData = UIImageJPEGRepresentation(screenshotImage, scale);
screenshotImage = [UIImage imageWithData: imageData];
}
return screenshotImage;
}
# 合并图片相关
+ (UIImage *)netexcept_verticalImageFromArray:(NSArray *)imagesArray
{
CGFloat sizeScale = 0.8;
UIImage *unifiedImage = [self mergeImages: imagesArray width:sizeScale*UIScreen.mainScreen.bounds.size.width];
if(unifiedImage){
NSData *imageData = UIImageJPEGRepresentation(unifiedImage, 0.4);
unifiedImage = [UIImage imageWithData: imageData];
}
return unifiedImage;
}
+ (UIImage *)mergeImages:(NSArray<UIImage *> *)images width:(CGFloat)width{
@autoreleasepool {
CGFloat totalHeight = 0;
CGFloat maxHeightOfImages = 0;
NSMutableArray<UIImage *> *resizedImages = [NSMutableArray arrayWithCapacity:images.count];
for (UIImage *image in images) {
CGFloat aspectRatio = image.size.width / image.size.height;
CGFloat newHeight = width / aspectRatio;
if (newHeight > maxHeightOfImages) {
maxHeightOfImages = newHeight;
}
totalHeight += newHeight;
UIGraphicsBeginImageContextWithOptions(CGSizeMake(width, newHeight), NO, 0.0);
[image drawInRect:CGRectMake(0, 0, width, newHeight)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[resizedImages addObject:newImage];
}
CGSize size = CGSizeMake(width, totalHeight);
UIGraphicsBeginImageContextWithOptions(size, NO, 0.0);
CGFloat y = 0;
for (UIImage *image in resizedImages) {
[image drawInRect:CGRectMake(0, y, width, image.size.height)];
y += image.size.height;
}
UIImage *resultImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return resultImage;
};
}