截圖
關(guān)于截圖有兩個方法:
- (void)renderInContext:(CGContextRef)ctx
: 作用于CALayer層的方法拨黔。將view的layer
渲染到當(dāng)前的繪制的上下文中钱磅。- (BOOL)drawViewHierarchyInRect:(CGRect)rect afterScreenUpdates:(BOOL)afterUpdates
: 作用于UIView的方法杖爽。對view進行一個快照警没,然后將快照渲染到當(dāng)前的上下文中。
renderInContext
就不說了锄俄,比較容易,只需要注意下UIGraphicsBeginImageContextWithOptions(CGSize size, BOOL opaque, CGFloat scale)
函數(shù)中的的size即可。
關(guān)鍵是- (BOOL)drawViewHierarchyInRect:(CGRect)rect afterScreenUpdates:(BOOL)afterUpdates
函數(shù)中多了一個描述位置的rect
參數(shù)千贯,容易弄不清這個rect
跟UIGraphicsBeginImageContextWithOptions(CGSize size, BOOL opaque, CGFloat scale)
函數(shù)中的size
的關(guān)系。之前一直沒有完全理解這兩個參數(shù)的區(qū)別搞坝,搜了很多資料發(fā)現(xiàn)都沒有人講清楚這兩個參數(shù)的含義搔谴,網(wǎng)上來來回回都是一些雷同的代碼,這次仔細測試研究了下桩撮,總結(jié)了下這兩個參數(shù)的含義敦第。
一般截圖的方法可以用例如下面這段代碼,對當(dāng)前控制的view進行截圖:
UIGraphicsBeginImageContextWithOptions(CGSizeMake(CGRectGetWidth(self.view.frame), CGRectGetHeight(self.view.frame)), YES, 0.0);
[self.view drawViewHierarchyInRect:self.view.bounds afterScreenUpdates:NO];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
先介紹下這兩個函數(shù)的含義:
UIGraphicsBeginImageContextWithOptions(CGSize size, BOOL opaque, CGFloat scale)
:開啟一個繪圖的上下文
size : size是指繪圖上下文的寬高店量,可以理解為要繪制圖形的畫布的大小
opaque : 是否透明芜果,如果傳YES,畫布的背景色為黑色融师,NO的時候右钾,畫布背景色是白色
scale:指的是繪制出來的圖片的像素比,決定了繪圖圖片的清晰度诬滩,一般填0.0霹粥,默認屏幕縮放比
- (BOOL)drawViewHierarchyInRect:(CGRect)rect afterScreenUpdates:(BOOL)afterUpdates
: 將要截屏的view繪制到當(dāng)前的上下文中。
rect
:指定圖片繪制的坐標(biāo)
afterUpdates
:截圖的瞬間是否將屏幕當(dāng)前的變更渲染進去
這個方法的是UIView
的方法疼鸟,截圖的目標(biāo)對象就是當(dāng)前方法的調(diào)用者后控。
例如[self.view drawViewHierarchyInRect:self.view.bounds afterScreenUpdates:NO];
就是對self.view
這個對象進行截圖,會把self.view
當(dāng)前的這個view全部截取下來然后繪制到當(dāng)前的上下文中生成圖片空镜,然后按照這個方法中指定的rect
為frame浩淘,以畫布為父視圖繪制到畫布中去捌朴。
注意:截圖截取的是drawViewHierarchyInRect
這個方法調(diào)用的view,而渲染出來的效果(圖片的位置和大姓懦)是由UIGraphicsBeginImageContextWithOptions(CGSize size, BOOL opaque, CGFloat scale)
中的size和- (BOOL)drawViewHierarchyInRect:(CGRect)rect afterScreenUpdates:(BOOL)afterUpdates
中的rect共同決定的砂蔽。
例如,我們要截取下面屏幕中的照片(請忽略被變形的照片...)
代碼如下:
注:
self.view
是當(dāng)前控制器的view
self.screenshotImgV
是上面的圖片的UIImageView
署惯。self.screenshotImgV
的frame是:(50, 37, 275, 489)
為了方便理解左驾,下面用畫布
來指代繪制的上下文
。
UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, 1, 0.0);
[self.screenshotImgV drawViewHierarchyInRect:self.screenshotImgV.frame afterScreenUpdates:NO];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
如上指定畫布
的大小為當(dāng)前self.view
的大小极谊,對self.screenshotImgV
進行截圖诡右,同時設(shè)置截圖后的圖片在畫布
中的位置為自身的frame。
截圖效果:
由于self.view
的大小是大于當(dāng)前self.screenshotImgV
的大小轻猖,所以對self.screenshotImgV
進行截圖繪制后帆吻,并不能充滿整個畫布
,只能占據(jù)其中的一部分咙边。而占據(jù)的位置依據(jù)的就是drawViewHierarchyInRect
方法中指定的rect
猜煮。這個例子中指定的是self.screenshotImgV.frame
,也即:(50, 37, 275, 489)
這個frame败许,所以就展示如上面效果王带。
下面我們來調(diào)整下指定的rect
, 讓其為self.screenshotImgV.bounds
檐束,看看效果辫秧。
UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, 1, 0.0);
[self.screenshotImgV drawViewHierarchyInRect:self.screenshotImgV.bounds afterScreenUpdates:NO];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
截圖效果:
如上圖,由于rect
用的是self.screenshotImgV.bounds
被丧,所以截圖的坐標(biāo)的x
和y
變?yōu)榱?code>(0, 0),也就是圖片的起始位置變?yōu)榱俗钭笊辖恰?/p>
下面我們來換一下绪妹,把畫布的大小設(shè)置為self.screenshotImgV
的大小甥桂,然后對self.view
進行截圖,看看效果:
UIGraphicsBeginImageContextWithOptions(self.screenshotImgV.bounds.size, 1, 0.0);
[self.view drawViewHierarchyInRect:self.screenshotImgV.frame afterScreenUpdates:NO];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
截圖效果:
如上邮旷,由于rect
指定為self.screenshotImgV.frame
黄选,只能從(50, 37)
這個位置開始渲染,所以在截圖的時候只能截取self.view
中的一部分婶肩,另外一部分超出畫布范圍截取不到办陷。如果frame的坐標(biāo)是從(0,0)
開始效果如何呢?
UIGraphicsBeginImageContextWithOptions(self.screenshotImgV.bounds.size, 1, 0.0);
[self.view drawViewHierarchyInRect:self.screenshotImgV.bounds afterScreenUpdates:NO];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
截圖效果:
發(fā)現(xiàn)剛好把self.view
完整的渲染出來了。這是因為UIGraphicsBeginImageContextWithOptions
方法中的size
和drawViewHierarchyInRect
中指定的rect.size
大小相同律歼,而且rect
的x
,y
都是(0,0)
民镜,所以截取的self.view
從左上角起始位置開始渲染,剛好能夠把整個畫布充滿险毁,全部渲染出來制圈。
通過上面的例子们童,應(yīng)你能弄清楚UIGraphicsBeginImageContextWithOptions(CGSize size, BOOL opaque, CGFloat scale)
中的size
和- (BOOL)drawViewHierarchyInRect:(CGRect)rect afterScreenUpdates:(BOOL)afterUpdates
中的rect
的意義和關(guān)系了吧。
當(dāng)然對于這個例子鲸鹦,如果我們想截取屏幕中的圖片self.screenshotImgV
慧库,只需要把尺寸都指定為self.screenshotImgV
的size
既可:
UIGraphicsBeginImageContextWithOptions(self.screenshotImgV.bounds.size, 1, 0.0);
[self.screenshotImgV drawViewHierarchyInRect:self.screenshotImgV.bounds afterScreenUpdates:NO];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
截圖效果:
裁剪圖片
裁剪圖片就是對當(dāng)前的圖片按照指定的大小范圍生成一個新的圖片,方法如下:
- (UIImage *)createImageWithRect:(CGRect)rect image:(UIImage *)clipImage {
CGImageRef imageRef = CGImageCreateWithImageInRect(clipImage.CGImage, rect);
UIImage *image = [UIImage imageWithCGImage:imageRef scale:clipImage.scale orientation:UIImageOrientationUp];
CGImageRelease(imageRef);
return image;
}
這里的rect
就是指定的裁剪范圍馋嗜。方法比較簡單齐板,沒有什么好解釋的。主要注意的是葛菇,這里rect
包含的x
,y
,width
,height
應(yīng)該是圖片的絕對尺寸乘以圖片的縮放因子甘磨,否則裁剪出來的圖片是不對的。如果圖片是1倍圖會沒什么問題熟呛,但是如果是2倍圖或者3倍圖宽档,要么可能尺寸不對,要么截出來的圖片很模糊庵朝。完整方法可改為:
- (UIImage *)createImageWithRect:(CGRect)rect image:(UIImage *)clipImage {
rect.origin.x *= clipImage.scale;
rect.origin.y *= clipImage.scale;
rect.size.width *= clipImage.scale;
rect.size.height *= clipImage.scale;
CGImageRef imageRef = CGImageCreateWithImageInRect(clipImage.CGImage, rect);
UIImage *image = [UIImage imageWithCGImage:imageRef scale:clipImage.scale orientation:UIImageOrientationUp];
CGImageRelease(imageRef);
return image;
}
其他注意點:
WKWebView
對于WKWebView使用
renderInContext
的方法進行截圖的時候吗冤, 當(dāng)WKWebView 執(zhí)行UIGraphicsGetCurrentContext()
的結(jié)果返回的nil,截圖失敗所以只能
UIView
的drawViewHierarchyInRect
的方法去截圖
UIVisualEffectView九府,高斯模糊蒙版
- 對于使用了
UIBlurEffect
進行高斯模糊的UIView進行截圖的時候椎瘟,如果對view.layer
使用renderInContext
的方法進行截圖,上面的高斯模糊蒙版會失真失效侄旬,可以改用drawViewHierarchyInRect
方法肺蔚,這樣能夠保持高斯模糊的效果。相對于renderInContext
是對view的layer
渲染到當(dāng)前的上下文中儡羔,drawViewHierarchyInRect
方法是對view進行一個快照宣羊,然后將快照渲染到當(dāng)前的上下文中。