Pertanyaan iOS: apa cara tercepat dan paling berkinerja untuk membuat tangkapan layar secara terprogram?


di aplikasi iPad saya, saya ingin membuat tangkapan layar dari UIView yang mengambil bagian besar layar. Sayangnya, sublihat sangat bersarang, sehingga perlu waktu lama untuk membuat tangkapan layar dan menganimasikan halaman yang melengkung sesudahnya.

Apakah ada cara yang lebih cepat daripada yang "biasa"?

UIGraphicsBeginImageContext(self.bounds.size);
[self.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *resultingImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

Jika memungkinkan, saya ingin menghindari caching atau restrukturisasi pandangan saya.


75
2017-11-01 08:42


asal


Jawaban:


Saya telah menemukan metode yang lebih baik yang menggunakan API snapshot bila memungkinkan.

Saya harap itu membantu.

class func screenshot() -> UIImage {
    var imageSize = CGSize.zero

    let orientation = UIApplication.shared.statusBarOrientation
    if UIInterfaceOrientationIsPortrait(orientation) {
        imageSize = UIScreen.main.bounds.size
    } else {
        imageSize = CGSize(width: UIScreen.main.bounds.size.height, height: UIScreen.main.bounds.size.width)
    }

    UIGraphicsBeginImageContextWithOptions(imageSize, false, 0)
    for window in UIApplication.shared.windows {
        window.drawHierarchy(in: window.bounds, afterScreenUpdates: true)
    }

    let image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return image!
}

Ingin tahu lebih banyak tentang iOS 7 Snapshots?

Versi Objective-C:

+ (UIImage *)screenshot
{
    CGSize imageSize = CGSizeZero;

    UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
    if (UIInterfaceOrientationIsPortrait(orientation)) {
        imageSize = [UIScreen mainScreen].bounds.size;
    } else {
        imageSize = CGSizeMake([UIScreen mainScreen].bounds.size.height, [UIScreen mainScreen].bounds.size.width);
    }

    UIGraphicsBeginImageContextWithOptions(imageSize, NO, 0);
    CGContextRef context = UIGraphicsGetCurrentContext();
    for (UIWindow *window in [[UIApplication sharedApplication] windows]) {
        CGContextSaveGState(context);
        CGContextTranslateCTM(context, window.center.x, window.center.y);
        CGContextConcatCTM(context, window.transform);
        CGContextTranslateCTM(context, -window.bounds.size.width * window.layer.anchorPoint.x, -window.bounds.size.height * window.layer.anchorPoint.y);
        if (orientation == UIInterfaceOrientationLandscapeLeft) {
            CGContextRotateCTM(context, M_PI_2);
            CGContextTranslateCTM(context, 0, -imageSize.width);
        } else if (orientation == UIInterfaceOrientationLandscapeRight) {
            CGContextRotateCTM(context, -M_PI_2);
            CGContextTranslateCTM(context, -imageSize.height, 0);
        } else if (orientation == UIInterfaceOrientationPortraitUpsideDown) {
            CGContextRotateCTM(context, M_PI);
            CGContextTranslateCTM(context, -imageSize.width, -imageSize.height);
        }
        if ([window respondsToSelector:@selector(drawViewHierarchyInRect:afterScreenUpdates:)]) {
            [window drawViewHierarchyInRect:window.bounds afterScreenUpdates:YES];
        } else {
            [window.layer renderInContext:context];
        }
        CGContextRestoreGState(context);
    }

    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return image;
}

110
2017-11-05 00:41




EDIT 3 Oktober 2013 Diperbarui untuk mendukung metode drawViewHierarchyInRect: afterScreenUpdates: super cepat baru di iOS 7.


Tidak. CALAYA renderInContext: sejauh yang saya tahu satu-satunya cara untuk melakukan ini. Anda dapat membuat kategori UIView seperti ini, untuk memudahkan Anda maju:

UIView + Screenshot.h

#import <UIKit/UIKit.h>

@interface UIView (Screenshot)

- (UIImage*)imageRepresentation;

@end

UIView + Screenshot.m

#import <QuartzCore/QuartzCore.h>
#import "UIView+Screenshot.h"

@implementation UIView (Screenshot)

- (UIImage*)imageRepresentation {

    UIGraphicsBeginImageContextWithOptions(self.bounds.size, YES, self.window.screen.scale);

    /* iOS 7 */
    if ([self respondsToSelector:@selector(drawViewHierarchyInRect:afterScreenUpdates:)])            
        [self drawViewHierarchyInRect:self.bounds afterScreenUpdates:NO];
    else /* iOS 6 */
        [self.layer renderInContext:UIGraphicsGetCurrentContext()];

    UIImage* ret = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();

    return ret;

}

@end

Dengan ini Anda mungkin bisa mengatakan [self.view.window imageRepresentation] dalam pengontrol tampilan, dan dapatkan tangkapan layar penuh dari aplikasi Anda. Ini mungkin mengecualikan statusbar sekalipun.

EDIT:

Dan boleh saya tambahkan. Jika Anda memiliki UIView dengan konten transparan, dan membutuhkan representasi gambar DENGAN konten yang mendasari juga, Anda dapat mengambil representasi gambar dari tampilan penampung dan memotong gambar itu, cukup dengan mengambil rect dari subview dan mengonversinya ke penampung memandang sistem koordinat.

[view convertRect:self.bounds toView:containerView]

Untuk mengkrop lihat jawaban untuk pertanyaan ini: Memotong UIImage


18
2017-11-01 10:25



iOS 7 memperkenalkan metode baru yang memungkinkan Anda menggambar hierarki tampilan ke dalam konteks grafis saat ini. Ini bisa digunakan untuk mendapatkan UIImage sangat cepat.

Diimplementasikan sebagai metode kategori UIView:

- (UIImage *)pb_takeSnapshot {
    UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, [UIScreen mainScreen].scale);

    [self drawViewHierarchyInRect:self.bounds afterScreenUpdates:YES];

    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return image;
}

Ini jauh lebih cepat daripada yang ada renderInContext: metode.

UPDATE UNTUK SWIFT: Ekstensi yang melakukan hal yang sama:

extension UIView {

    func pb_takeSnapshot() -> UIImage {
        UIGraphicsBeginImageContextWithOptions(self.bounds.size, false, UIScreen.mainScreen().scale);

        self.drawViewHierarchyInRect(self.bounds, afterScreenUpdates: true)

        // old style: self.layer.renderInContext(UIGraphicsGetCurrentContext())

        let image = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        return image;
    }
}

9
2017-09-20 20:44



Saya menggabungkan jawaban untuk fungsi tunggal yang akan berjalan untuk setiap versi iOS, bahkan untuk perangkat retina atau non-retensi.

- (UIImage *)screenShot {
    if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)])
        UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, NO, [UIScreen mainScreen].scale);
    else
        UIGraphicsBeginImageContext(self.view.bounds.size);

    #ifdef __IPHONE_7_0
        #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000
            [self.view drawViewHierarchyInRect:self.view.bounds afterScreenUpdates:YES];
        #endif
    #else
            [self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
    #endif

    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return image;
}

3
2018-01-09 06:14



Apa yang Anda minta sebagai alternatif adalah membaca GPU (karena layar ini dibuat dari sejumlah tampilan tembus pandang), yang juga merupakan operasi yang lambat.


1
2017-11-07 23:34



Bagi saya pengaturan InterpolationQuality berjalan jauh.

CGContextSetInterpolationQuality(ctx, kCGInterpolationNone);

Jika Anda snapshotting gambar yang sangat rinci, solusi ini mungkin tidak dapat diterima. Jika Anda snapshotting teks Anda tidak akan melihat perbedaannya.

Ini mengurangi waktu untuk mengambil snap shot secara signifikan serta membuat gambar yang mengkonsumsi jauh lebih sedikit memori.

Ini masih bermanfaat dengan metode drawViewHierarchyInRect: afterScreenUpdates:.


1
2017-10-11 21:26