您的位置:首页 > 移动开发 > IOS开发

iOS中正确的截屏姿势(代码)

2014-12-22 10:45 537 查看
来自 http://blog.0xbbc.com/2014/12/ios%E4%B8%AD%E6%AD%A3%E7%A1%AE%E7%9A%84%E6%88%AA%E5%B1%8F%E5%A7%BF%E5%8A%BF/

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

第一种

这是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;

}

第二种

这是在比较常见的截图方法,不过不支持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 <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;

}

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

typedef void *
IOMobileFramebufferConnection;

In the reversed header, IOMobileFramebufferConnection is typedef'd toio_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。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: