昨天写了个用到截屏功能的插件,结果问题不断,今天终于解决好了,把debug过程中所有尝试过的截屏方法都贴出来吧~

第一种

这是iOS 3时代开始就被使用的方法,它被废止于iOS 7。iOS的私有方法,效率很高。

#import
extern "C" CGImageRef UIGetScreenImage();
UIImage * screenshot(void) NS_DEPRECATED_IOS(3_0,7_0);
UIImage * screenshot(){
    UIImage *image = [UIImage imageWithCGImage:UIGetScreenImage()];
    return image;
}

第二种

这是在比较常见的截图方法,不过不支持Retina屏幕。

UIImage * screenshot(UIView *);
UIImage * screenshot(UIView *view){
    UIGraphicsBeginImageContext(view.frame.size);
    [view.layer renderInContext:UIGraphicsGetCurrentContext()];
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return image;
}

第三种

从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;
}

第四种

或许你会说有时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;
}

第五种

@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的实例。

第六种

#import #import #import #import #import 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;
}

需要注意的是,第五种方法需要修改一下IOMobileFramebuffer的头文件。

typedef void * IOMobileFramebufferConnection;

In the reversed header, IOMobileFramebufferConnection is typedef'd to io_connect_t, which is typedef'd to io_object_t, which is mach_port_t, which is __darwin_mach_port_t, which is __darwin_mach_port_name_t, which is __darwin_natural_t, which is unsigned int! Int just happens to be pointer-sized on 32-bit, but is not under 64-bit。

——StackoverFlow

修改好的头文件顺便也丢上来吧,解压后放在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。

iOS中正确的截屏姿势的更多相关文章

  1. ios摇一摇截屏代码

    #import "ViewController.h" @interface ViewController () @end @implementation ViewControlle ...

  2. iOS - Quartz 2D 手势截屏绘制

    1.绘制手势截屏 具体实现代码见 GitHub 源码 QExtension QTouchClipView.h @interface QTouchClipView : UIView /** * 创建手势 ...

  3. .NET Core 中正确使用 HttpClient 的姿势

    为了更方便在服务端调用 HTTP 请求,微软在 .NET Framework 4.x 的时候引入了 HttpClient.但 HttpClient 有很多严重问题,一直饱受诟病,比如 InfoQ 的这 ...

  4. unity中编辑器直接截屏代码

    using UnityEngine; using System.Collections; using System.Windows.Forms; public class screenshots : ...

  5. iOS检测用户截屏并获取所截图片

    iOS检测用户截屏并获取所截图片 微信可以检测到用户截屏行为(Home + Power),并在稍后点击附加功能按钮时询问用户是否要发送刚才截屏的图片,这个用户体验非常好.在iOS7之前, 如果用户截屏 ...

  6. ios中iframe页面出现白屏问题

    最近用ionic3开发的一个项目在ios中出现了白屏的问题 banner轮播图跳转网页 使用了iframe 但是却时不时的出现白屏现象 在android中一切正常 网上查资料发现 是因为ios不允许访 ...

  7. iOS 中捕获截屏操作

    转自:iOS知识小集 在iOS 7后,苹果提供了UIApplicationUserDidTakeScreenshotNotification通知来告诉App用户做了截屏操作.苹果的描述如下: // T ...

  8. iOS中的截屏(屏幕截屏及scrollView或tableView的全部截屏)

    iOS中的截屏(屏幕截屏及scrollView或tableView的全部截屏) 2017.03.16 12:18* 字数 52 阅读 563评论 4喜欢 2 1. 截取屏幕尺寸大小的图片并保存至相册 ...

  9. iOS 中对各种视图的截屏以及分享

    1.一个第三方的工具,主要是对表视图.滚动视图.视图的扩展,用法也很简单 image = [tableview screenshot]; 2.然后将截的图片分享出去,在分享的时候,因为多个地方用到了截 ...

随机推荐

  1. Linux下环境变量设置

    分类 Linux下的环境变量按生存周期来划分,可以划分为两种: 1)永久的:需要修改配置文件, 变量永久生效 2)临时的:直接在终端使用export命令声明即可,但是关闭shell后失效. 设置方法 ...

  2. MVC开发Markdown编辑器(1)

    MVC markdown MVC开发Markdown编辑器(1) 前言 安装 解析 结束语 前言 想在近段时间通过mvc开发个人博客,编辑器希望是markdown风格的,这样写文字会很方便.首先先解决 ...

  3. MDK建立STM32F103*开发模板

    一.整体流程 1.获取ST库--STM32F10x_StdPeriph_Lib_V3.5.0 2.新建文件夹并加载文件 3.新建工程 4.给工程添加组 5.设置"Target Option& ...

  4. IO-04. 混合类型数据格式化输入

    /** *A4-IO-04. 混合类型数据格式化输入 *C语言实现 *测试已通过 */ #include "stdio.h" int main() { float m1,m2; i ...

  5. BZOJ 1567: [JSOI2008]Blue Mary的战役地图( 二分答案 + hash )

    二分答案, 然后用哈希去判断... ------------------------------------------------------------------------- #include ...

  6. jvm的搭建

    首先先 说明一下接下来要用到的,环境变量中的path和classpath的区别 1.path路径用来告诉计算机.exe文件的路径,classpath路径是用来告诉计算机.class文件的路径 2.系统 ...

  7. ASP.NET MVC5路由系统机制详细讲解

    请求一个ASP.NET mvc的网站和以前的web form是有区别的,ASP.NET MVC框架内部给我们提供了路由机制,当IIS接受到一个请求时,会先看是否请求了一个静态资源(.html,css, ...

  8. 基于jenkins搭建一个持续集成服务器

    1 引言 1.1 编写目的 指导质量管理部,业务测试组同事进行Jenkins环境部署,通过Jenkins解决测试环境不可控,开发测试环境不一致等问题. 1.2 使用对象 质量管理部.基础研发部,集成部 ...

  9. js中的稀疏数组和密集数组

    原文地址: http://www.2ality.com/2012/06/dense-arrays.html 一般来说JavaScript中的数组都是稀疏的,也就是说数组中的元素与元素之间是由空格的,因 ...

  10. win10设置vscode的终端为管理员权限

    右击vscode 点击属性选择兼容性,勾选 “以管理员身份运行此程序” 确定即可!