iOS中正确的截屏姿势

依旧是从博客搬运过来的,博客原文
论坛这边因为没开放HTML标签,所以会失去代码高亮。
如果更习惯看有高亮的代码的话(比如我),也可以直接去看博客原文
如果有其他方法,欢迎补充~

[list]
*]第一种
这是iOS 3时代开始就被使用的方法,它被废止于iOS 7。iOS的私有方法,效率很高。#import <QuartzCore/QuartzCore.h>
extern “C” CGImageRef UIGetScreenImage();
UIImage * screenshot(void) NS_DEPRECATED_IOS(3_0,7_0);
UIImage * screenshot(){
UIImage *image = [UIImage imageWithCGImage:UIGetScreenImage()];
return image;
}[list]
*]第二种
[/list]这是在比较常见的截图方法,不过不支持Retina屏幕。UIImage * screenshot(UIView *);
UIImage * screenshot(UIView *view){
UIGraphicsBeginImageContext(view.frame.size);
[view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}[list]
*]第三种
[/list]从iPhone 4、iPod Touch 4开始,Apple逐渐采用Retina屏幕,于是在iOS 4的SDK中我们有了,上面的截图方法也自然变成了这样。UIImage * screenshot(UIView *) NS_AVAILABLE_IOS(4_0);
UIImage * screenshot(UIView *view){
if(UIGraphicsBeginImageContextWithOptions != NULL)
{
UIGraphicsBeginImageContextWithOptions(view.frame.size, NO, 0.0);
} else {
UIGraphicsBeginImageContext(view.frame.size);
}
[view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}[list]
*]第四种
[/list]或许你会说有时Hook的是一个按钮的方法,用第三个方法的话,根本找不到view来传值,不过还好,iOS 7又提供了一些UIScreen的API。UIImage * screenshot(void) NS_AVAILABLE_IOS(7_0);UIImage * screenshot(){
UIView * view = [UIScreen mainScreen] snapshotViewAfterScreenUpdates:YES];
if(UIGraphicsBeginImageContextWithOptions != NULL)
{
UIGraphicsBeginImageContextWithOptions(view.frame.size, NO, 0.0);
} else {
UIGraphicsBeginImageContext(view.frame.size);
}
[view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}[list]
*]第五种
[/list]@interface SBScreenShotter : NSObject

  • (id)sharedInstance;
  • (void)saveScreenshot:(_Bool)arg1;
    @end然后直接[SBScreenShotter sharedInstance] saveScreenshot:YES];一道白光之后,咱们就模拟了用户截屏的动作,不过这个方法在只需要截屏时比较好,如果要对屏幕录像(其实就是不断截图)的话,那不得闪瞎了。。而且我们也拿不到UIImage的实例去拼成一个视频呀。即使通过Hook别的类拿到UIImage的实例,这个私有API的效率大概也是达不到30FPS的视频要求的。那么现在我们有5种方法了,第一种是私有API,私有API通常效率和质量都比Documented API的好,可是它在iOS 7以后就被废除了啊,就没有别的了吗?答案当然是————有的!用Private Framework来完成这项任务!直接走底层拿屏幕的缓冲数据,然后生成UIImage的实例。[list]
    ]第六种
    [/list]#import <IOMobileFrameBuffer.h>
    #import <UIKit/UIKit.h>
    #import <QuartzCore/QuartzCore.h>
    #import <IOKit/IOKit.h>
    #import <IOSurface/IOSurface.h>extern “C” IOReturn IOSurfaceLock(IOSurfaceRef buffer, uint32_t options, uint32_t seed);
    extern “C” IOReturn IOSurfaceUnlock(IOSurfaceRef buffer, uint32_t options, uint32_t seed);
    extern “C” size_t IOSurfaceGetWidth(IOSurfaceRef buffer);
    extern “C” size_t IOSurfaceGetHeight(IOSurfaceRef buffer);
    extern “C” IOSurfaceRef IOSurfaceCreate(CFDictionaryRef properties);
    extern “C” void IOSurfaceGetBaseAddress(IOSurfaceRef buffer);
    extern “C” size_t IOSurfaceGetBytesPerRow(IOSurfaceRef buffer);extern const CFStringRef kIOSurfaceAllocSize;
    extern const CFStringRef kIOSurfaceWidth;
    extern const CFStringRef kIOSurfaceHeight;
    extern const CFStringRef kIOSurfaceIsGlobal;
    extern const CFStringRef kIOSurfaceBytesPerRow;
    extern const CFStringRef kIOSurfaceBytesPerElement;
    extern const CFStringRef kIOSurfacePixelFormat;enum
    {
    kIOSurfaceLockReadOnly =0x00000001,
    kIOSurfaceLockAvoidSync =0x00000002
    };UIImage * screenshot(void);UIImage * screenshot(){
    IOMobileFramebufferConnection connect;
    kern_return_t result;
    CoreSurfaceBufferRef screenSurface = NULL;
    io_service_t framebufferService = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching(“AppleH1CLCD”));
    if(!framebufferService)
    framebufferService = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching(“AppleM2CLCD”));
    if(!framebufferService)
    framebufferService = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching(“AppleCLCD”));result = IOMobileFramebufferOpen(framebufferService, mach_task_self(), 0, &connect);
    result = IOMobileFramebufferGetLayerDefaultSurface(connect, 0, &screenSurface); uint32_t aseed;
    IOSurfaceLock((IOSurfaceRef)screenSurface, 0x00000001, &aseed);
    size_t width = IOSurfaceGetWidth((IOSurfaceRef)screenSurface); size_t height = IOSurfaceGetHeight((IOSurfaceRef)screenSurface);
    CFMutableDictionaryRef dict;
    size_t pitch = width
    4, size = width
    height
    4; int bPE=4; char pixelFormat[4] = {‘A’,‘R’,‘G’,‘B’};
    dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
    CFDictionarySetValue(dict, kIOSurfaceIsGlobal, kCFBooleanTrue);
    CFDictionarySetValue(dict, kIOSurfaceBytesPerRow, CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &pitch));
    CFDictionarySetValue(dict, kIOSurfaceBytesPerElement, CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &bPE));
    CFDictionarySetValue(dict, kIOSurfaceWidth, CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &width));
    CFDictionarySetValue(dict, kIOSurfaceHeight, CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &height));
    CFDictionarySetValue(dict, kIOSurfacePixelFormat, CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, pixelFormat));
    CFDictionarySetValue(dict, kIOSurfaceAllocSize, CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &size)); IOSurfaceRef destSurf = IOSurfaceCreate(dict);
    IOSurfaceAcceleratorRef outAcc;
    IOSurfaceAcceleratorCreate(NULL, 0, &outAcc); IOSurfaceAcceleratorTransferSurface(outAcc, (IOSurfaceRef)screenSurface, destSurf, dict, NULL);
    IOSurfaceUnlock((IOSurfaceRef)screenSurface, kIOSurfaceLockReadOnly, &aseed);
    CFRelease(outAcc); CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, IOSurfaceGetBaseAddress(destSurf), (width * height * 4), NULL); CGImageRef cgImage = CGImageCreate(width, height, 8,
    8
    4, IOSurfaceGetBytesPerRow(destSurf),
    CGColorSpaceCreateDeviceRGB(), kCGImageAlphaNoneSkipFirst|kCGBitmapByteOrder32Little, provider, NULL, YES, kCGRenderingIntentDefault); UIImage *image = [UIImage imageWithCGImage:cgImage];
    return image;
    }

[/list]
需要注意的是,第五种方法需要修改一下IOMobileFramebuffer的头文件。typedef void * IOMobileFramebufferConnection;
修改好的头文件。headers.zip (61.8 KB)解压后放在Project的根目录下。如果你使用的是theos的话,记得在Makefile里写上,YOUR_TWEAK_NAME_PRIVATE_FRAMEWORKS = IOSurface IOKit IOMobileFramebuffer**YOUR_TWEAK_NAME_CFLAGS = -I./headers/ -I./headers/IOSurface如果是XCode上的Logos Tweak的话,在Build Settings -> Search Paths -> Header Search Paths里面添加一项:$(PROJECT_DIR)/YOUR_PROJECT_NAME/headers, 搜索方式为recursive. 最后在Build Phases里Link上IOSurface IOKit IOMobileFramebuffer这三个私有Framework。

1 Like

Discuz的编辑器略坑呀。。
转代码格式之后直接显示HTML代码了。。。

多谢楼主分享,不过看到帖子题目(姿势),再看到内容,我才知道我想错了。

好像游戏里面不能截图
楼主有办法在游戏里截图吗

多谢楼主分享,如果是连续截取的话,那种效率好点,比如內存的使用.

楼主,第4种方法在IOS8里用你的代码是空白的图片呢。
以下代码会返回正常的

 
    UIView *snapshot = [UIScreen mainScreen] snapshotViewAfterScreenUpdates:YES];
    CGSize outputSize = CGSizeMake([UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height);
    UIGraphicsBeginImageContextWithOptions(outputSize, NO, 0);
    [snapshot drawViewHierarchyInRect:CGRectMake(0.0, 0.0, outputSize.width, outputSize.height) afterScreenUpdates:YES];   //貌似IOS8要用这个方法
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

在游戏里面截图可以呀,写一个tweak然后调用截图就可以了,只不过只能截最上层的!