你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發基礎 >> iOS中正確的截屏姿勢

iOS中正確的截屏姿勢

編輯: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。

                                 ——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。

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved