77 votes

iOS: quel est le moyen le plus rapide et le plus performant de réaliser une capture d'écran par programme?

Dans mon application iPad, j'aimerais faire une capture d'écran d'un UIView prenant une grande partie de l'écran. Malheureusement, les sous-vues sont assez profondément imbriquées, il faut donc beaucoup de temps pour faire la capture d'écran et animer une page courbée par la suite.

Y a-t-il un moyen plus rapide que le moyen "habituel"?

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

Si possible, j'aimerais éviter de mettre en cache ou de restructurer mon point de vue.

Merci beaucoup pour votre aide!

111voto

NSElvis Points 3083

Mise à jour

J'ai trouvé une meilleure méthode qui utilise le nouveau iOS 7 instantané API chaque fois que possible.

La magie est ici:

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

J'espère que cela aide.

+ (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;
}

Vous voulez savoir de plus sur iOS 7 Instantanés?


Vieille réponse

J'ai trouvé la réponse à votre question dans la Technique Q&r de la Bibliothèque de développement iOS, donc, ici, il va:

Chaque UIWindow (héritée de UIView) et UIView est soutenu par une CALayer. Le CALayer/-renderInContext: méthode permet de rendre une couche et ses sous-couches d'un contexte graphique. Ainsi, pour prendre un instantané de la la totalité de l'écran, vous pouvez itérer sur chaque fenêtre de l'écran et rendre sa hiérarchie de couches à destination d'un contexte. Une fois fini, vous pouvez obtenir la copie d'écran de l'image via le UIGraphicsGetImageFromCurrentImagecontext fonction, comme illustré ci-dessous.

Utilisation de Core Animation d'Api, vous devez ajouter le QuartzCore cadre dans votre projet Xcode et comprennent la QuartzCore en-tête, comme indiqué dans la la liste suivante:


Liste 1: Inclure la QuartzCore en-tête

#import <QuartzCore/QuartzCore.h>

Liste 2: Obtenir une capture d'écran de l'image

- (UIImage*)screenshot 
{
    // Create a graphics context with the target size
    // On iOS 4 and later, use UIGraphicsBeginImageContextWithOptions to take the scale into consideration
    // On iOS prior to 4, fall back to use UIGraphicsBeginImageContext
    CGSize imageSize = [[UIScreen mainScreen] bounds].size;
    if (NULL != UIGraphicsBeginImageContextWithOptions)
        UIGraphicsBeginImageContextWithOptions(imageSize, NO, 0);
    else
        UIGraphicsBeginImageContext(imageSize);

    CGContextRef context = UIGraphicsGetCurrentContext();

    // Iterate over every window from back to front
    for (UIWindow *window in [[UIApplication sharedApplication] windows]) 
    {
        if (![window respondsToSelector:@selector(screen)] || [window screen] == [UIScreen mainScreen])
        {
            // -renderInContext: renders in the coordinate space of the layer,
            // so we must first apply the layer's geometry to the graphics context
            CGContextSaveGState(context);
            // Center the context around the window's anchor point
            CGContextTranslateCTM(context, [window center].x, [window center].y);
            // Apply the window's transform about the anchor point
            CGContextConcatCTM(context, [window transform]);
            // Offset by the portion of the bounds left of and above the anchor point
            CGContextTranslateCTM(context,
                                  -[window bounds].size.width * [[window layer] anchorPoint].x,
                                  -[window bounds].size.height * [[window layer] anchorPoint].y);

            // Render the layer hierarchy to the current context
            [[window layer] renderInContext:context];

            // Restore the context
            CGContextRestoreGState(context);
        }
    }

    // Retrieve the screenshot image
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();

    return image;
}

18voto

Trenskow Points 1748

10voto

Klaas Points 2995

iOS 7 a introduit une nouvelle méthode qui vous permet de tracer une hiérarchie de vues dans le contexte graphique actuel. Ceci peut être utilisé pour obtenir un UIImage très rapidement.

Mis en œuvre comme méthode de catégorie sur UIView :

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

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

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

Il est considérablement plus rapide que la méthode renderInContext: existante.

UPDATE FOR SWIFT : Une extension qui fait la même chose:

 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;
    }
}
 

3voto

Hemang Points 3422

J'ai combiné les réponses à une seule fonction qui sera exécutée pour toutes les versions d'iOS, même pour les appareils avec ou sans rétention.

 - (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;
}
 

1voto

David Dunham Points 4620

Ce que vous demandez comme alternative, c'est de lire le GPU (puisque l'écran est composé à partir d'un nombre illimité de vues translucides), ce qui est aussi une opération intrinsèquement lente.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X