I'm trying to render/draw the snapshotView of a UIView in contex to get a UIImage.then set it as a CAlayer.contents. I try this method to get snapshotView:
snapshotViewAfterScreenUpdates:
then convert UIView to UIImage:
UIGraphicsBeginImageContext(view.bounds.size);
[view.layer renderInContext:UIGraphicsGetCurrentContext()];
//I'm trying this method too
[view drawViewHierarchyInRect:view.bounds afterScreenUpdates:NO];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
the problem is I'm using the code above, but I get a empty image.
If you need a snapshot UIImage in the first place just use the method in your second code block. Create a UIView's category like
@implementation UIView (takeSnapshot)
- (UIImage *)takeASnapshot {
UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, [UIScreen mainScreen].scale);
[self drawViewHierarchyInRect:self.bounds afterScreenUpdates:YES];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
@end
That's enough. You don't need snapshot a view and convert it to a image. In your case this process might cause the problem
Had a similar issue when building interactive animations with floating snapshots
. Here what we did:
UIView extension:
extension UIView {
public func snapshot(scale: CGFloat = 0, isOpaque: Bool = false, afterScreenUpdates: Bool = true) -> UIImage? {
UIGraphicsBeginImageContextWithOptions(bounds.size, isOpaque, scale)
drawHierarchy(in: bounds, afterScreenUpdates: afterScreenUpdates)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
public enum CASnapshotLayer: Int {
case `default`, presentation, model
}
/// The method drawViewHierarchyInRect:afterScreenUpdates: performs its operations on the GPU as much as possible
/// In comparison, the method renderInContext: performs its operations inside of your app’s address space and does
/// not use the GPU based process for performing the work.
/// https://stackoverflow.com/a/25704861/1418981
public func caSnapshot(scale: CGFloat = 0, isOpaque: Bool = false,
layer layerToUse: CASnapshotLayer = .default) -> UIImage? {
var isSuccess = false
UIGraphicsBeginImageContextWithOptions(bounds.size, isOpaque, scale)
if let context = UIGraphicsGetCurrentContext() {
isSuccess = true
switch layerToUse {
case .default:
layer.render(in: context)
case .model:
layer.model().render(in: context)
case .presentation:
layer.presentation()?.render(in: context)
}
}
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return isSuccess ? image : nil
}
}
Usage example (inside interactive animation):
private func makeSnapshot(view: UIView, snapshotLayer: UIView.CASnapshotLayer) -> UIView? {
// There is 3 ways for taking snapshot:
// 1. Replicate view: `view.snapshotView(afterScreenUpdates: true)`
// 2. Draw Hierarchy: `view.drawHierarchy(in: bounds, afterScreenUpdates: true)`
// 3. Render Layer: `layer.render(in: context)`
//
// Only #3 is working reliable during UINavigation controller animated transitions.
// For modally presented UI also trick by combining #1 and #2 seems works.
// I.e. `view.snapshotView(afterScreenUpdates: true).snapshot()`
//
// If this call causes error listed below, then this indicate that something wrong with one of the views layout.
// [Snapshotting] View (_UIReplicantView) drawing with afterScreenUpdates:YES inside CoreAnimation commit is not supported.
// See also: https://stackoverflow.com/a/29676207/1418981
if let image = view.caSnapshot(layer: snapshotLayer) {
let imageView = ImageView(image: image)
imageView.clipsToBounds = true
imageView.contentMode = .scaleAspectFit
shapshots[view] = image
return imageView
}
return nil
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With