iOS中正确的截屏姿势
昨天写了个用到截屏功能的插件,结果问题不断,今天终于解决好了,把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。
修改好的头文件顺便也丢上来吧,解压后放在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中正确的截屏姿势的更多相关文章
- ios摇一摇截屏代码
#import "ViewController.h" @interface ViewController () @end @implementation ViewControlle ...
- iOS - Quartz 2D 手势截屏绘制
1.绘制手势截屏 具体实现代码见 GitHub 源码 QExtension QTouchClipView.h @interface QTouchClipView : UIView /** * 创建手势 ...
- iOS 点击按钮截屏
@interface CaptureViewController () @property (nonatomic, strong) UIImageView *backgrounView; //控制器背 ...
- .NET Core 中正确使用 HttpClient 的姿势
为了更方便在服务端调用 HTTP 请求,微软在 .NET Framework 4.x 的时候引入了 HttpClient.但 HttpClient 有很多严重问题,一直饱受诟病,比如 InfoQ 的这 ...
- unity中编辑器直接截屏代码
using UnityEngine; using System.Collections; using System.Windows.Forms; public class screenshots : ...
- iOS检测用户截屏并获取所截图片
iOS检测用户截屏并获取所截图片 微信可以检测到用户截屏行为(Home + Power),并在稍后点击附加功能按钮时询问用户是否要发送刚才截屏的图片,这个用户体验非常好.在iOS7之前, 如果用户截屏 ...
- ios中iframe页面出现白屏问题
最近用ionic3开发的一个项目在ios中出现了白屏的问题 banner轮播图跳转网页 使用了iframe 但是却时不时的出现白屏现象 在android中一切正常 网上查资料发现 是因为ios不允许访 ...
- Android8.1 MTK平台 截屏功能分析
前言 涉及到的源码有 frameworks\base\services\core\java\com\android\server\policy\PhoneWindowManager.java vend ...
- Python: 截屏
最近项目中想实现截屏功能,使用的笔记本是高清屏,实际屏幕设置成了150%,所以在截屏的时候遇到个各种问题. 最开始使用PIL ImageGrab来截取屏幕,如果本来是100%的设置没有问题,能截取到全 ...
随机推荐
- 如何用cocoapods 来管理项目中的第三方框架?
一.安装 1.如果以前安装过,升级到10.10后工作不正常,可以先删除cocoapods $ sudo gem uninstall cocoapods ----------------------- ...
- BZOJ 2466: [中山市选2009]树( 高斯消元 )
高斯消元解异或方程组...然后对自由元进行暴搜.树形dp应该也是可以的... ------------------------------------------------------------- ...
- gridview回顾
第一看asp.net是在做项目之前,感觉收获也很大,第二次看gridview是在做完项目之后对GridView的回顾,这次的感觉是:我需要多想点,知识直到用时方觉少.直入正题吧,看gridview. ...
- R语言学习笔记之外部文件读取
在win32位的系统下,RODBC包内的函数是可以直接运行的,但在win64位的系统则不支持! 1.读取外部文件read.table()---csv,txt,excel 最基本函数是read.tabl ...
- 新唐M0特点分析
1,价格低,05x系列0.6-1.5美金,1xx系列1.5-3.5美金:2,性能好,最新32位CORTEX-M0的ARM核,唯一可工作到+5.5V的CORTEX-M0:3,速度快,CPU核能跑到50M ...
- python开发_大小写转换,首字母大写,去除特殊字符
这篇blog主要是总结我们在平常开发过程中对字符串的一些操作: #字母大小写转换 #首字母转大写 #去除字符串中特殊字符(如:'_','.',',',';'),然后再把去除后的字符串连接起来 #去除' ...
- oracle数据类型和对应的java类型
由于 实体类里面 使用的是 double ,生成的Oracle 用的JDBC 类型为java.sql.Types.FLOAT 所以Oracle 数据类型为 float. 如果想保留两位小数 实体类 ...
- 通过自定义window来实现提示框效果
#import <UIKit/UIKit.h>#import <Foundation/Foundation.h> @interface ZMStatuBarHud : NSOb ...
- 几种经典排序算法的JS实现
一.冒泡排序 function BubbleSort(array) { var length = array.length; for (var i = length - 1; i > 0; i- ...
- tomcat配置没啥难的啊
总体上就是: 1.下载jdk并安装. 2.下载tomcat 3.分别设置好环境变量. 4.cmd java -version 如果成功,证明java jdk配置OK startup.bat 如果成功 ...