27

I've often read that using a CALayer rather than a UIImageView is an performance boost when it comes to heavy image usage. That makes sense, because UIImageView causes 3 copies of the image in memory, which is needed for Core Animation. But in my case I don't use Core Animation.

How can I assign a UIImage (or its image data) to a CALayer and then display it?

nevan king
  • 112,709
  • 45
  • 203
  • 241
  • 6
    Where have you read this? UIViews in general are lightweight wrappers around CALayers, and I've not seen much of a difference between them in regards to memory or display performance. I'm also not sure if I buy the 3 copies of an image in memory argument. In any case, CALayers are indeed part of Core Animation (thus the CA prefix). – Brad Larson Oct 14 '09 at 18:02

3 Answers3

55
UIImage*    backgroundImage = [UIImage imageNamed:kBackName];
CALayer*    aLayer = [CALayer layer];
CGFloat nativeWidth = CGImageGetWidth(backgroundImage.CGImage);
CGFloat nativeHeight = CGImageGetHeight(backgroundImage.CGImage);
CGRect      startFrame = CGRectMake(0.0, 0.0, nativeWidth, nativeHeight);
aLayer.contents = (id)backgroundImage.CGImage;
aLayer.frame = startFrame;

or in a Swift playground (you will have to provide your own PNG image in the Playground's resource file. I'm using the example of "FrogAvatar".)

  //: Playground - noun: a place where people can play
import UIKit

if let backgroundImage = UIImage(named: "FrogAvatar") // you will have to provide your own image in your playground's Resource file
{
    let height = backgroundImage.size.height
    let width = backgroundImage.size.width

    let aLayer = CALayer()
    let startFrame = CGRect(x: 0, y: 0, width: width, height: height)

    let aView = UIView(frame: startFrame)
    aLayer.frame = startFrame
    aLayer.contentsScale = aView.contentScaleFactor
    aLayer.contents = backgroundImage.cgImage
    aView.layer.addSublayer(aLayer)
    aView // look at this via the Playground's  eye icon
}

An image as the content of a CALayer embedded in a UIView

Glenn Howes
  • 5,447
  • 2
  • 25
  • 29
  • thanks! why do you write (id)backgroundImage and not just backgroundImage? Shouldn't the compiler recognize that you have a UIImage there? Or is the CGImage property otherwise not recognized? Maybe an missing import? –  Oct 14 '09 at 12:58
  • I tried it. The most interesting thins is missing though: How to get it on screen? I want to avoid an UIViews as much as possible. –  Oct 14 '09 at 14:38
  • You'll need to add the CALayer as a sublayer of some UIView's layer. Each layer does not need its own view, but the layer hierarchy does need to reside in a view somewhere so that it can be drawn to the screen. – Brad Larson Oct 14 '09 at 18:04
  • 5
    The cast to id is needed because CGImageRef is not defined in the headers as descending from id, but that is how the contents property of CALayer is defined. – Glenn Howes Oct 15 '09 at 06:05
  • 3
    Just wanted to mention that you should divide native image size by `[[UIScreen mainScreen] scale]` – Nekto Feb 22 '13 at 01:58
12

ARC version requires a different cast:

self.myView.layer.contents = (__bridge id) self.myImage.CGImage;
nevan king
  • 112,709
  • 45
  • 203
  • 241
1
CALayer *layer = [[CALayer alloc] init];
layer.contents = (__bridge id _Nullable)([UIImage imageNamed:@"REWIFISocketOff"].CGImage);
Denis Kutlubaev
  • 15,320
  • 6
  • 84
  • 70