如何在iOS上以编程方式进行屏幕截图

159
我想要将屏幕上的图像截屏并保存到照片库中。
20个回答

233

如果要检查是否为视网膜显示器,请使用以下代码片段:

#import <QuartzCore/QuartzCore.h> 

if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) {
    UIGraphicsBeginImageContextWithOptions(self.window.bounds.size, NO, [UIScreen mainScreen].scale);
} else {
    UIGraphicsBeginImageContext(self.window.bounds.size);
}

[self.window.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
NSData *imageData = UIImagePNGRepresentation(image);
if (imageData) {
    [imageData writeToFile:@"screenshot.png" atomically:YES];
} else {
    NSLog(@"error while taking screenshot");
}

14
请注意,这不会捕获某些类型的屏幕内容,比如OpenGL ES图层。而UIGetScreenImage()可以实现,这也是华盈所指的。 - Brad Larson
12
亲爱的大家,不要忘记做 #import <QuartzCore/QuartzCore.h>。 - Enrico Susatyo
5
这个图像存储在哪里? - Burhanuddin Sunelwala
@BurhanuddinSunelwala,我最终找到了一个解决方案,添加以下代码UIImageWriteToSavedPhotosAlbum([UIImage imageWithData:data], nil, nil, nil);你可以在照片相册路径中找到你的图片。 - Jignesh B
4
@wladimir-palant: 键盘怎么办?因为当我截屏用户输入的界面时,键盘不会被截取,我该如何在截屏中添加键盘呢? - g212gs
显示剩余7条评论

49

以下方法也适用于OPENGL对象

//iOS7 or above
- (UIImage *) screenshot {

    CGSize size = CGSizeMake(your_width, your_height);

    UIGraphicsBeginImageContextWithOptions(size, NO, [UIScreen mainScreen].scale);

    CGRect rec = CGRectMake(0, 0, your_width, your_height);
    [_viewController.view drawViewHierarchyInRect:rec afterScreenUpdates:YES];

    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return image;
}

5
UIGetScreenImage 是一个私有 API。 - jonahb
非常感谢您!您绝对得到了我的支持。在尝试多种其他方法后,似乎drawViewHierarchy是使用javafx ui在robovm中进行屏幕截图的唯一方法。 - Christo Smal
这是每次都有效的!!!我在屏幕上有很多3D变换,其他解决方案会破坏我的截图。这个像应该的那样工作。非常感谢! - AndrewK
1
如果视图控制器在导航控制器中,则不会捕获导航栏。 - Radu Simionescu
是的,drawViewHierarchyInRect 比 renderInContext 快 :-) - alones

20
UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, self.view.opaque, 0.0);
[self.myView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

NSData *imageData = UIImageJPEGRepresentation(image, 1.0 ); //you can use PNG too
[imageData writeToFile:@"image1.jpeg" atomically:YES];

1
对于使用PNG的注释点赞。它是无损的,而且在质量=1.0的情况下比JPEG更小。 - Rob
1
它在iOS 7中无法使用NavigationBar,而是留下了空白空间。 - Ali Sufyan

14
- (UIImage*) getGLScreenshot {
    NSInteger myDataLength = 320 * 480 * 4;

    // allocate array and read pixels into it.
    GLubyte *buffer = (GLubyte *) malloc(myDataLength);
    glReadPixels(0, 0, 320, 480, GL_RGBA, GL_UNSIGNED_BYTE, buffer);

    // gl renders "upside down" so swap top to bottom into new array.
    // there's gotta be a better way, but this works.
    GLubyte *buffer2 = (GLubyte *) malloc(myDataLength);
    for(int y = 0; y <480; y++)
    {
        for(int x = 0; x <320 * 4; x++)
        {
            buffer2[(479 - y) * 320 * 4 + x] = buffer[y * 4 * 320 + x];
        }
    }

    // make data provider with data.
    CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, buffer2, myDataLength, NULL);

    // prep the ingredients
    int bitsPerComponent = 8;
    int bitsPerPixel = 32;
    int bytesPerRow = 4 * 320;
    CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
    CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault;
    CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;

    // make the cgimage
    CGImageRef imageRef = CGImageCreate(320, 480, bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpaceRef, bitmapInfo, provider, NULL, NO, renderingIntent);

    // then make the uiimage from that
    UIImage *myImage = [UIImage imageWithCGImage:imageRef];
    return myImage;
}

- (void)saveGLScreenshotToPhotosAlbum {
    UIImageWriteToSavedPhotosAlbum([self getGLScreenshot], nil, nil, nil);  
}

来源


1
为什么在 320*480 之上,我的数据长度要乘以 4 呢? - Jab
2
这似乎非常愚蠢,他们没有为我们提供更简单的方法来做到这一点。 - Jab
好的,只需将比例因子乘以宽度和高度即可 :) - JonLOo
这个代码只在OpenGL应用上有效吗?在普通应用程序上会出现黑色图像...我该怎么使用它? - orthehelper
1
@Aistina,你可以使用UIImage *myImage = [[UIImage alloc] initWithCGImage:imageRef scale:1.0 orientation:UIImageOrientationDownMirrored];,这样就不需要'buffer2'了。顺便提醒一下,看起来你忘记了内存管理,没有使用free()或者release()... - Oleg Trakhman
显示剩余4条评论

11

使用 Swift 语言

func captureScreen() -> UIImage
{

    UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, false, 0);

    self.view.drawViewHierarchyInRect(view.bounds, afterScreenUpdates: true)

    let image: UIImage = UIGraphicsGetImageFromCurrentImageContext()

    UIGraphicsEndImageContext()

    return image
}

10

iOS10 版本以后,这变得更加简单了。UIKit 提供了 UIGraphicsImageRenderer,让你能够完成绘图任务,而不需要处理像色深和图片缩放这样的配置,或者管理 Core Graphics 上下文。

...就像完成绘图任务一样,而无需处理如颜色深度和图像比例等配置,也无需管理 Core Graphics 上下文。

Apple Docs - UIGraphicsImageRenderer

现在你可以这样做:

let renderer = UIGraphicsImageRenderer(size: someView.bounds.size)

let image = renderer.image(actions: { context in
    someView.drawHierarchy(in: someView.bounds, afterScreenUpdates: true)
})

这里的许多回答在大多数情况下对我都有效。但是当尝试拍摄ARSCNView的快照时,我只能使用上述方法来完成。虽然值得注意的是此时ARKit仍处于beta版,Xcode也处于beta 4版。


9
看起来你现在可以使用UIGetScreenImage(),请参见此帖子

9
无法再在iOS 4中允许这样做。 - sudo rm -rf

7
这将保存屏幕截图并返回屏幕截图。
-(UIImage *)capture{
    UIGraphicsBeginImageContext(self.view.bounds.size);
    [self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
    UIImage *imageView = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    UIImageWriteToSavedPhotosAlbum(imageView, nil, nil, nil); //if you need to save
    return imageView;
}

5

如果您想要全屏显示(除状态栏外),我认为以下代码片段会对您有所帮助。如有需要,请将AppDelegate替换为您的应用代理名称。

- (UIImage *)captureFullScreen {

    AppDelegate *_appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;

    if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) {
        // for retina-display
        UIGraphicsBeginImageContextWithOptions(_appDelegate.window.bounds.size, NO, [UIScreen mainScreen].scale);
        [_appDelegate.window drawViewHierarchyInRect:_appDelegate.window.bounds afterScreenUpdates:NO];
    } else {
        // non-retina-display
        UIGraphicsBeginImageContext(_bodyView.bounds.size);
        [_appDelegate.window drawViewHierarchyInRect:_appDelegate.window.bounds afterScreenUpdates:NO];
    }

    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return image;
}

终于有一个添加导航栏的了!如果可以的话,我会投两票的!干杯,伙计。 - mmsarquis

4

我回答这个问题是因为它被高度关注,并且有许多答案,还有Swift和Obj-C。

免责声明这不是我的代码,也不是我的答案,只是为了帮助来到这里的人快速找到答案。有原始答案的链接,以便给予应有的荣誉!如果您使用他们的答案,请通过+1来尊重原始答案!


使用 QuartzCore

#import <QuartzCore/QuartzCore.h> 

if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) {
    UIGraphicsBeginImageContextWithOptions(self.window.bounds.size, NO, [UIScreen mainScreen].scale);
} else {
    UIGraphicsBeginImageContext(self.window.bounds.size);
}

[self.window.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
NSData *imageData = UIImagePNGRepresentation(image);
if (imageData) {
    [imageData writeToFile:@"screenshot.png" atomically:YES];
} else {
    NSLog(@"error while taking screenshot");
}

在Swift中

func captureScreen() -> UIImage
{

    UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, false, 0);

    self.view.drawViewHierarchyInRect(view.bounds, afterScreenUpdates: true)

    let image: UIImage = UIGraphicsGetImageFromCurrentImageContext()

    UIGraphicsEndImageContext()

    return image
}

注意:由于编程的性质,可能需要进行更新,请编辑或告诉我!*另外,如果我没有包含值得包括的答案/方法,请随时让我知道!

1
这个回答似乎没有任何贡献。你只是复制粘贴了页面上已有的两个答案,并将结果发布为自己的答案。 - user2357112
我的意图并不是要把他们的答案归功于我,而是为了在一个答案中包含Swift和Obj-C,以便人们更快地找到。我也参考了相应的答案。如果这样做不合适,我会删除我的答案并恢复其中一个。 - Jab
我正在使用 ObjC 解决方案。对我来说,在 "self" 后面添加 ".view",但结果是 100%。这是黄金。 - samouray
它也可以用于捕获演示流。谢谢。 - YSR fan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接