How to render view into image faster?
No. In iOS6, renderInContext: is the only way. It is slow. It uses the CPU.
Ways to render UIKit content
renderInContext:
[view.layer renderInContext:UIGraphicsGetCurrentContext()];
- Requires iOS 2.0. It runs in the CPU.
- It doesn't capture views with non-affine transforms, OpenGL, or video content.
- If an animation is running, you can have the option of capturing:
-
view.layer
, which captures the final frame of the animation. -
view.presentationLayer
, which captures the current frame of the animation .
-
snapshotViewAfterScreenUpdates:
UIView *snapshot = [view snapshotViewAfterScreenUpdates:YES];
- Requires iOS 7.
- It is the fastest method.
- The view
contents
are immutable. Not good if you want to apply an effect. - It captures all content types (UIKit, OpenGL, or video).
resizableSnapshotViewFromRect:afterScreenUpdates:withCapInsets
[view resizableSnapshotViewFromRect:rect afterScreenUpdates:YES withCapInsets:edgeInsets]
- Requires iOS 7.
- Same as
snapshotViewAfterScreenUpdates:
but with resizable insets.content
is also immutable.
drawViewHierarchyInRect:afterScreenUpdates:
[view drawViewHierarchyInRect:rect afterScreenUpdates:YES];
- Requires iOS 7.
- It draws in the current context.
- According to session 226 it is faster than
renderInContext:
.
See WWDC 2013 Session 226 Implementing Engaging UI on iOS about the new snapshotting APIs.
If it is any help, here is some code to discard capture attempts while one is still running.
This throttles block execution to one at a time, and discards others. From this SO answer.
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
dispatch_queue_t renderQueue = dispatch_queue_create("com.throttling.queue", NULL);
- (void) capture {
if (dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW) == 0) {
dispatch_async(renderQueue, ^{
// capture
dispatch_semaphore_signal(semaphore);
});
}
}
What is this doing?
- Create a semaphore for one (1) resource.
- Create a serial queue.
-
DISPATCH_TIME_NOW
means the timeout is none, so it returns non zero immediately on red light. Thus, not executing the if content. - If green light, run the block asynchronously, and set green light again.
Related videos on Youtube
Comments
-
NOrder almost 2 years
I'm making magnifier app, which allows an user touch the screen and move his finger, there will be a magnifier with his finger path. I implement it with take a screenshot and assign the image to magnifier image view, as following:
CGSize imageSize = frame.size; UIGraphicsBeginImageContextWithOptions(imageSize, NO, 0.0); CGContextRef c = UIGraphicsGetCurrentContext(); CGContextScaleCTM(c, scaleFactor, scaleFactor); CGContextConcatCTM(c, CGAffineTransformMakeTranslation(-frame.origin.x, -frame.origin.y)); [self.layer renderInContext:c]; UIImage *screenshot = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return screenshot;
the problem is that
self.layer renderInContext
is slow, so user feel not smooth when he is moving his finger. and I tried to runself.layer renderInContext
in other thread, however, it makes the magnifier image looked weird because the image in magnifier showed delay.is there any better way to render view into image? does
renderInContext:
use GPU?-
Rob over 10 yearsIn answer to your question, according to Polishing Your User Interface Rotations,
renderInContext
is performed in CPU.
-
-
NOrder over 10 yearsinstead of renderInContext, is there any way to do it?
-
Jano over 10 yearsNo. If it is any help, try rasterizing the layer you are about to render and use a serial queue with a semaphore so it does one renderInContext: at a time. I'm posting some code above. But again,
renderInContext:
is the only way to capture UIKit content, and it is slow. -
Victor Engel over 10 yearsJust noting that the original question asked about rendering into an image, and this answer shows rendering into a view.
-
Jano over 10 yearsrenderInContext and drawViewHierarchyInRect:afterScreenUpdates: draw into a context that you can turn into an UIImage with UIGraphicsGetImageFromCurrentImageContext(). You can also use drawViewHierarchyInRect to draw the view to the context. I'll try to add some sample code when I have the time.
-
Shaun Budhram over 9 yearsdoes snapshotViewAfterScreenUpdates require the layer to actually have been visible to capture anything? I would like to snapshot the contents of a view without ever actually having drawn it to the screen.
-
felippeduran almost 9 yearsJust to note that
snapshotViewAfterScreenUpdates:
will not work if you plan to get an image from the resulting UIView: stackoverflow.com/questions/20203682/…. -
Infaz over 8 yearsIm using "drawViewHierarchyInRect" to get image from a UIWebview,but im always getting blank white image,pls assist me to fix this issue
-
arlomedia about 7 yearsAlthough not officially deprecated, renderInContext is returning a solid gray image in my app on the latest hardware (iPhone 7 and 9.7" iPad Pro). I reported this to Apple as bug 28804475, but haven't received a reply.