一. 原因
今天做项目遇到这样一个问题,困扰了一阵,最后请教了一位同事才解决的:
CGSize imageSize = CGSizeZero;
UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
if (UIInterfaceOrientationIsPortrait(orientation))
imageSize = [UIScreen mainScreen].bounds.size;
else
imageSize = CGSizeMake([UIScreen mainScreen].bounds.size.height, [UIScreen mainScreen].bounds.size.width);
UIGraphicsBeginImageContextWithOptions(imageSize, NO, 0);
CGContextRef context = UIGraphicsGetCurrentContext();
for (UIWindow *window in [[UIApplication sharedApplication] windows])
{
CGContextSaveGState(context);
CGContextTranslateCTM(context, window.center.x, window.center.y);
CGContextConcatCTM(context, window.transform);
CGContextTranslateCTM(context, -window.bounds.size.width * window.layer.anchorPoint.x, -window.bounds.size.height * window.layer.anchorPoint.y);
if (orientation == UIInterfaceOrientationLandscapeLeft)
{
CGContextRotateCTM(context, M_PI_2);
CGContextTranslateCTM(context, 0, -imageSize.width);
}
else if (orientation == UIInterfaceOrientationLandscapeRight)
{
CGContextRotateCTM(context, -M_PI_2);
CGContextTranslateCTM(context, -imageSize.height, 0);
} else if (orientation == UIInterfaceOrientationPortraitUpsideDown) {
CGContextRotateCTM(context, M_PI);
CGContextTranslateCTM(context, -imageSize.width, -imageSize.height);
}
if ([window respondsToSelector:@selector(drawViewHierarchyInRect:afterScreenUpdates:)])
{
[window drawViewHierarchyInRect:window.bounds afterScreenUpdates:YES];
}
else
{
[window.layer renderInContext:context];
}
CGContextRestoreGState(context);
}
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
NSData * data = UIImagePNGRepresentation(image);
//UIImage * png= [UIImage imageWithData:data];
UIImage * png = [[UIImage alloc] initWithData:data scale:image.scale];
return png;
这个函数是截屏的代码,复制粘贴即可使用
问题在于最后几句:
当得到了一个image后,在执行完UIImagePNGRepresentation对它进行png的转换后,使用 [UIImage imageWithData:data];我发现得到的png的size居然翻倍了.也就是说:
image的scale为:{375,667} 转为png后居然成了{750,1334}, 不能忍啊
原因是什么呢?UIImagePNGRepresentation函数是没有问题的.我在再次由它得到UIImage时,需要设置scale属性为源image的scale,也就是2,两者要保持一致.
因为[UIImage imageWithData:data];的scale默认是1,所以用这个函数就会出现不预期的效果,要用 initWithData:scale这个函数
二. scale学习
看到这里,感觉scale还是蛮重要的一个属性,之前图片用的粗浅,也没怎么关注
scale的描述为:image的扩大倍数.
- 如果一张图片名字包含:....@2x 则这张图片的scale属性默认就被设置为了2.其他的图片都是默认的scale,为1
- image的size属性(它是逻辑尺寸),乘以scale属性,就是这个图片实际的像素值.
所以像上面的例子中:
所以理解一下:
一开始我画出来的image,像素为{750,1334 }
PNG转出来的imagedata. 像素还是{750,1334 }.
我在initWithData:scale:中设置它的scale,则它的size就是:像素/scale.
所以如果原图image的像素是{750,1334 },scale设置为2,则png的size是{375,667};如果我把scale设置为1,则png的size是{750,1334}.而默认的scale是1,所以这就弄清楚了为什么我使用UIImagePNGRepresentation对原图进行png的转换后,size会扩大一倍的原因.
三. 画图学习
好了,进一步研究下我的原图像素在哪里设置.实话说这段截屏代码是参考的网上的,自己大略看了下.现在仔细看一下
1. UIGraphicsBeginImageContextWithOptions(size,opaque,scale)
这个函数创建一个基于位图(像素)的画图context,并把它push进图形context的栈.
1.1 它使用view采用的坐标,即左上角为原点,向下和右延伸,scale参数作用在所画的图上,会把位图的scale属性标记为这个值.如果传0.0,则这个参数会用设备的scale,像我的iphone6s是2 所以我UIGraphicsBeginImageContextWithOptions那里传入scale为0,结果出来的图scale是2的.
1.2 size是传入的逻辑size
1.3 文档告诉我们,和该参数配套使用的有:
获取图片:UIGraphicsGetImageFromCurrentImageContext
用完后要出栈:必须用UIGraphicsEndImageContext,而不是UIGraphicsPopContext
获取这个栈中的context:UIGraphicsGetCurrentContext
1.4 还有UIGraphicsBeginImageContext,参数都用默认的
2. UIGraphicsGetCurrentContext
一般当前的图形context都是nil或者默认值.
2.1 对于一个UIView来说,在进入它的drawRect: 方法之前,系统会自动给它的图形context栈push一个context.我们直接用UIGraphicsGetCurrentContext获取就是了
2.2 那不是在UIView中画图的时候怎么办呢?我们要手动的调用UIGraphicsPushContext方法或者1中的UIGraphicsBeginImageContextWithOptions,给栈中push一个context,再用这个方法取出来用.
3. CGContextSaveGState
把当前context的statecopy一份,存入context的state栈
每个context都维护了一个自己的栈,存放state,state是啥东西?有点多:
图2-1
好吧,后面几个函数意思没能领会住,果断复习layer的知识去了,搞清楚了回来补充.
4. renderInContext
把调用者的层画到context中,层是view展示的样子,这个方法即把view画到了context中
5. UIGraphicsGetImageFromCurrentImageContext
从当前context中取出图片
这个函数,必须是和基于位图的context一起使用,也就是context需要是UIGraphicsBeginImageContext函数创建的,不然返回nil
6. UIGraphicsEndImageContext
将基于位图的context弹出栈.它必须和UIGraphicsBeginImageContext配对使用
所以基于上面6个函数的思想,还有比较简单的截屏办法
这是我的参考
四. 检测截屏
这个向通知中心注册一个通知就好了
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(userDidTakeScreenshot:)
name:UIApplicationUserDidTakeScreenshotNotification object:nil];
响应函数:
-(void) userDidTakeScreenshot:(NSNotification* )notification{
好了,暂时告一段落,我去复习下layer和画图的知识
网友评论