admin管理员组

文章数量:1398999

Apple provides several methods to take a UIView snapshot as a UIImage, such as UIGraphicsImageRenderer and drawHierarchy(in:). However, none of them are fast enough for my needs.

So, I decided to use a private API for this purpose.

I tried using the approach from , but I always end up with a white image. How can I fix the white image issue?

import UIKit
import IOSurface

typealias CARenderServerRenderDisplayType = @convention(c) (UInt32, CFString, IOSurfaceRef, Int, Int) -> Void

// Function to load the private API
func getCARenderServerRenderDisplay() -> CARenderServerRenderDisplayType? {
    let handle = dlopen("/System/Library/Frameworks/QuartzCore.framework/QuartzCore", RTLD_LAZY)
    guard let symbol = dlsym(handle, "CARenderServerRenderDisplay") else {
        print("CARenderServerRenderDisplay not found")
        return nil
    }
    return unsafeBitCast(symbol, to: CARenderServerRenderDisplayType.self)
}

extension UIView {
    func fastScreenshot() -> UIImage? {
        let screenSize = UIScreen.main.bounds.size
        let scale = UIScreen.main.scale
        let width = Int(screenSize.width * scale)
        let height = Int(screenSize.height * scale)
        
        let bytesPerElement = 4
        let bytesPerRow = bytesPerElement * width
        let totalBytes = bytesPerRow * height
        
        let properties: [CFString: Any] = [
            kIOSurfaceIsGlobal: true,
            kIOSurfaceBytesPerElement: bytesPerElement,
            kIOSurfaceBytesPerRow: bytesPerRow,
            kIOSurfaceWidth: width,
            kIOSurfaceHeight: height,
            kIOSurfacePixelFormat: 0x42475241, // ARGB format
            kIOSurfaceAllocSize: totalBytes
        ]
        
        guard let surface = IOSurfaceCreate(properties as CFDictionary) else { return nil }
        
        // Lock the surface
        IOSurfaceLock(surface, [], nil)
        
        // Fast screenshot capture using a private API (similarly to the method from the code you shared)
        if let renderFunction = getCARenderServerRenderDisplay() {
            renderFunction(0, "LCD" as CFString, surface, 0, 0)
        } else {
            print("Failed to find CARenderServerRenderDisplay")
        }
        
        // Get the raw pixel data
        let baseAddress = IOSurfaceGetBaseAddress(surface)
        let data = Data(bytes: baseAddress, count: totalBytes)
        
        // Unlock the surface
        IOSurfaceUnlock(surface, [], nil)
        
        // Convert raw data into CGImage
        guard let provider = CGDataProvider(data: data as CFData) else { return nil }
        
        guard let imageRef = CGImage(
            width: width,
            height: height,
            bitsPerComponent: 8,
            bitsPerPixel: 8 * bytesPerElement,
            bytesPerRow: bytesPerRow,
            space: CGColorSpaceCreateDeviceRGB(),
            bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.first.rawValue),
            provider: provider,
            decode: nil,
            shouldInterpolate: false,
            intent: .defaultIntent
        ) else { return nil }
        
        return UIImage(cgImage: imageRef)
    }
}

本文标签: uikitFastest way to capture a UIView snapshot as a UIImage using a private APIStack Overflow