Code snippet - cropping a screenshot

Recently I have been having fun with the UIView drawViewHierarchyInRect: method (yes, my life is one big party). It is a useful way of producing images within an app for use as backgrounds, with social media posts etc.

Doco

The method provides an image of the full view, but this is not always what you want. For example you might want to crop the image to remove controls, status bars etc before using it. So how to crop the image?

By the way, don't be misled by the "InRect" in the method name. It refers to the location of the drawing rather than an area of the view to be rendered. So drawViewHierarchyInRect:croppingRect … will not crop the image as you might hope, but will draw an image of the entire view, and draw it in the specified rectangle.

The code below is one way to capture and crop an image of a view.

Code

The cropping rectangle is specified in the coordinate system of the view which leads to the one subtlety here. The operation to crop the image (CGImageCreateWithImageInRect) works on pixels rather than points, so the cropping rectangle needs to be scaled by the scale factor of the display (2 in the case of retina devices).

(If you are thinking that the scaling operation lacks a bit of CS cred feel free to use CGRectApplyAffineTransform and CGAffineTransformMakeScale, but also perhaps think about getting out more)

Update

on 2014-03-20 12:58 by John

The code above comes with one warning (other than "use at your own risk" obviously) - do not use it anywhere in the initial display of the application delagate window's root view controller view, at least not without one small change.

The issue is the "drawViewHierarchy ... afterScreenUpdates:YES". If you invoke this while loading the root view controllers view from application:didFinishLaunchingWithOptions: you will end up with the "Application windows are expected to have a root view controller at the end of the application launch" error and a blank screen.

This is hard to track down because the root view controller is set, and the view is loaded. It all looks like it should be OK but the afterScreenUpdates:YES causes the view controller and it's view not to be correctly recognised and handled.

If you need to use code like this at launch, change it to "afterScreenUpdates:NO" and all should be well.