AsyncDisplayKit is now Texture! LEARN MORE

Texture

Image Modification Blocks

Many times, operations that would affect the appearance of an image you’re displaying are big sources of main thread work. Naturally, you want to move these to a background thread.

By assigning an imageModificationBlock to your imageNode, you can define a set of transformations that need to happen asynchronously to any image that gets set on the imageNode.

SwiftObjective-C
_backgroundImageNode.imageModificationBlock = ^(UIImage *image) {
	UIImage *newImage = [image applyBlurWithRadius:30
										 tintColor:[UIColor colorWithWhite:0.5 alpha:0.3]
							 saturationDeltaFactor:1.8
							 			 maskImage:nil];
	return newImage ? newImage : image;
};

//some time later...

_backgroundImageNode.image = someImage;

The image named “someImage” will now be blurred asynchronously before being assigned to the imageNode to be displayed.

Adding image effects

The most efficient way to add image effects is by leveraging the imageModificationBlock block. If a block is provided it can perform drawing operations on the image during the display phase. As display is happening on a background thread it will not block the main thread.

In the following example we assume we have an avatar node that will be setup in init of a supernode and the image of the node should be rounded. We provide the imageModificationBlock and in there we call a convenience method that transforms the image passed in into a circular image and return it.

SwiftObjective-C
- (instancetype)init
{
// ...
  _userAvatarImageNode.imageModificationBlock = ^UIImage *(UIImage *image) {
    CGSize profileImageSize = CGSizeMake(USER_IMAGE_HEIGHT, USER_IMAGE_HEIGHT);
    return [image makeCircularImageWithSize:profileImageSize];
  };
  // ...
}

The actual drawing code is nicely abstracted away in an UIImage category and looks like the following:

SwiftObjective-C
@implementation UIImage (Additions)
- (UIImage *)makeCircularImageWithSize:(CGSize)size
{
  // make a CGRect with the image's size
  CGRect circleRect = (CGRect) {CGPointZero, size};

  // begin the image context since we're not in a drawRect:
  UIGraphicsBeginImageContextWithOptions(circleRect.size, NO, 0);

  // create a UIBezierPath circle
  UIBezierPath *circle = [UIBezierPath bezierPathWithRoundedRect:circleRect cornerRadius:circleRect.size.width/2];

  // clip to the circle
  [circle addClip];

  // draw the image in the circleRect *AFTER* the context is clipped
  [self drawInRect:circleRect];

  // get an image from the image context
  UIImage *roundedImage = UIGraphicsGetImageFromCurrentImageContext();

  // end the image context since we're not in a drawRect:
  UIGraphicsEndImageContext();

  return roundedImage;
}
@end

The imageModificationBlock is very handy and can be used to add all kind of image effects, such as rounding, adding borders, or other pattern overlays, without extraneous display calls.

Edit on GitHub