In meiner iPad-App möchte ich einen Screenshot eines UIView machen, der einen großen Teil des Bildschirms einnimmt. Leider sind die Unteransichten ziemlich tief verschachtelt, so dass es zu lange dauert, den Screenshot zu erstellen und anschließend eine Seite zu animieren, die sich kräuselt.
Gibt es einen schnelleren Weg als den "üblichen"?
UIGraphicsBeginImageContext(self.bounds.size);
[self.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *resultingImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
Wenn möglich, möchte ich vermeiden, meine Ansicht zwischenzuspeichern oder neu zu strukturieren.
Antworten:
Ich habe eine bessere Methode gefunden, die nach Möglichkeit die Snapshot-API verwendet.
Ich hoffe, es hilft.
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! }
Möchten Sie mehr über iOS 7-Snapshots erfahren?
Objective-C-Version:
+ (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; }
quelle
BEARBEITEN 3. Oktober 2013 Aktualisiert, um die neue superschnelle Methode drawViewHierarchyInRect: afterScreenUpdates: in iOS 7 zu unterstützen.
Nein. CALayers renderInContext: ist meines Wissens der einzige Weg, dies zu tun. Sie können eine UIView-Kategorie wie diese erstellen, um sich die Zukunft zu erleichtern:
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
Auf diese Weise können Sie möglicherweise
[self.view.window imageRepresentation]
in einem Ansichts-Controller sagen , dass Sie einen vollständigen Screenshot Ihrer App erhalten. Dies könnte jedoch die Statusleiste ausschließen.BEARBEITEN:
Und darf ich hinzufügen. Wenn Sie eine UIView mit transparentem Inhalt haben und auch eine Bilddarstellung mit dem darunter liegenden Inhalt benötigen, können Sie eine Bilddarstellung der Containeransicht abrufen und dieses Bild zuschneiden, indem Sie einfach das Rechteck der Unteransicht nehmen und es in den Container konvertieren Ansichten Koordinatensystem.
Zum Zuschneiden siehe Antwort auf diese Frage: Zuschneiden eines UIImage
quelle
afterScreenUpdates:YES
, aber ansonsten funktioniert es großartig.In iOS 7 wurde eine neue Methode eingeführt, mit der Sie eine Ansichtshierarchie in den aktuellen Grafikkontext zeichnen können. Dies kann verwendet werden, um eine
UIImage
sehr schnelle zu bekommen .Implementiert als Kategoriemethode für
UIView
:- (UIImage *)pb_takeSnapshot { UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, [UIScreen mainScreen].scale); [self drawViewHierarchyInRect:self.bounds afterScreenUpdates:YES]; UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return image; }
Es ist erheblich schneller als die bestehende
renderInContext:
Methode.UPDATE FÜR SWIFT : Eine Erweiterung, die dasselbe tut:
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; } }
quelle
self.drawViewHierarchyInRect(self.bounds, afterScreenUpdates: true)
verursacht es für einen Moment einen seltsamen Anzeigefehler, während es ausgeführt wird? Ich habe nicht das gleiche Problem mitself.layer.renderInContext(UIGraphicsGetCurrentContext())
.Ich habe die Antworten auf einzelne Funktionen kombiniert, die für alle iOS-Versionen ausgeführt werden können, auch für Netzhaut- oder Nicht-Retain-Geräte.
- (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; }
quelle
Für mich war das Einstellen der InterpolationQuality ein langer Weg.
CGContextSetInterpolationQuality(ctx, kCGInterpolationNone);
Wenn Sie sehr detaillierte Bilder aufnehmen, ist diese Lösung möglicherweise nicht akzeptabel. Wenn Sie Schnappschüsse erstellen, werden Sie den Unterschied kaum bemerken.
Dies verkürzte die Zeit für die Aufnahme erheblich und machte ein Bild, das viel weniger Speicherplatz beanspruchte.
Dies ist bei der Methode drawViewHierarchyInRect: afterScreenUpdates: weiterhin von Vorteil.
quelle
Als Alternative möchten Sie die GPU lesen (da der Bildschirm aus einer beliebigen Anzahl durchscheinender Ansichten besteht), was ebenfalls von Natur aus langsam ist.
quelle