55

I must add a UIImageView as subview of MapView. To do this I created a layer above the MapView. In this layer I want to put my image, but I get a white rectangle and nothing else. My image is not visible.

This is the code:

- (void)viewDidLoad
{
    //......

    CALayer *layer = [CALayer layer];
    layer.backgroundColor = [[UIColor whiteColor] CGColor];

    if(UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
    {
        layer.bounds = CGRectMake(self.mapView.bounds.origin.x,
                                  self.mapView.bounds.origin.y, 80, 300);
    }
    else
    {
        layer.bounds = CGRectMake(self.mapView.frame.origin.x,
                                  self.mapView.frame.origin.y, 150, 700);
    }

    layer.contents = (id)[UIImage imageNamed:@"myImage.png"];
    //the name is correct but  in the output the image is not visible

    [[self.mapView layer] addSublayer:layer];
    [layer setNeedsDisplay];
}
Pramod More
  • 1,220
  • 2
  • 22
  • 51
Ortensia C.
  • 4,666
  • 11
  • 43
  • 70

7 Answers7

168

This is a general answer for the sake of future viewers. It is based on the question title rather than the details of the original question.

How to add a UIImage to a CALayer

You can add an image to a view's layer simply by using its contents property:

myView.layer.contents = UIImage(named: "star")?.cgImage
  • Note that the UIImage needs to be converted to a CGImage.

If you wish to add the image in its own layer, you can do it like this:

let myLayer = CALayer()
let myImage = UIImage(named: "star")?.cgImage
myLayer.frame = myView.bounds
myLayer.contents = myImage
myView.layer.addSublayer(myLayer)

Modifying the appearance

The above code produces a view like this. The light blue is the UIView and the dark blue star is the UIImage.

enter image description here

As you can see, though, it looks pixelated. This is because the UIImage is smaller than the UIView so it is being scaled to fill the view, which is the default it you don't specify anything else.

The examples below show variations on the layer's contentsGravity property. The code looks like this:

myView.layer.contents = UIImage(named: "star")?.cgImage
myView.layer.contentsGravity = kCAGravityTop
myView.layer.isGeometryFlipped = true

In iOS, you may want to set the isGeometryFlipped property to true if you are doing anything with top or bottom gravity, otherwise it will be the opposite of what you expect. (Only the gravity is flipped vertically, not the content rendering. If you are having trouble with the content being flipped, see this answer.)

There are two UIView examples below for every contentsGravity setting, one view is larger than the UIImage and the other is smaller. This way you can see the effects of the scaling and gravity.

kCAGravityResize

This is the default.

enter image description here

kCAGravityResizeAspect

enter image description here

kCAGravityResizeAspectFill

enter image description here

kCAGravityCenter

enter image description here

kCAGravityTop

enter image description here

kCAGravityBottom

enter image description here

kCAGravityLeft

enter image description here

kCAGravityRight

enter image description here

kCAGravityTopLeft

enter image description here

kCAGravityTopRight

enter image description here

kCAGravityBottomLeft

enter image description here

kCAGravityBottomRight

enter image description here

Related

Community
  • 1
  • 1
Suragch
  • 484,302
  • 314
  • 1,365
  • 1,393
  • Great answer. But how do you do kCAGravityResizeAspectFill and kCAGravityTop at the same time? – tcurdt Sep 21 '16 at 18:10
  • 2
    adjusting the contentsRect to the right ratio does the trick. Another option is to use an UIImageView as UIView maskView. – tcurdt Oct 14 '16 at 22:33
  • fantastic tutorial buddy @suragch Took me forever to find out that .gravity is where you set aspect fit/fill/etc. Note that interestingly the current Apple doco is plain wrong, it doesn't mention those options! the main doco page only mentions resize/center/flipped. Weird! – Fattie Aug 07 '19 at 18:18
  • 1
    addition for everyone: layer.contentsGravity = view.contentMode – dotrinh PM Sep 03 '21 at 09:31
  • To prevent the image from being rendered x2 or x3 times bigger than it should, you should set the CALayer's scale to match the screen's scale: layer.contentsScale = UIScreen.main.scale – nalexn Jan 03 '22 at 13:21
47

it has to be

layer.contents = (id)[UIImage imageNamed:@"myImage.png"].CGImage;

You can only put a CGImage into a layer, not an UIImage directly.

Bastian
  • 10,403
  • 1
  • 31
  • 40
9

I removed

 [layer setNeedsDisplay];

I do not know why, but it works!

Ortensia C.
  • 4,666
  • 11
  • 43
  • 70
  • 3
    I guess the setNeedsDisplay made the layer to redraw the content and so deleting the image. – Bastian Nov 08 '12 at 15:22
  • 2
    Plus one for this. It definitely triggers a redraw (what I thought it needed), but it throws away `content` before redrawing. – InkGolem Oct 11 '18 at 16:10
4

You need to set the frame of the CALayer properly, because the default is CGRectZero.

Soul Clinic
  • 1,189
  • 12
  • 18
3

2020, full simple examples

In this example, the image is simply the size of the overall view:

class ImageyView: UIView {
    
    private lazy var im: CALayer = {
        let l = CALayer()
        l.contents = UIImage(named: "some_background")?.cgImage
        l.contentsGravity = .resizeAspect
        layer.addSublayer(l)
        return l
    }()
    
    override func layoutSubviews() {
        super.layoutSubviews()
        im.frame = bounds
    }
}

In this example it's a Button, with small icon you're adding at the top left:

class BroadcastButton: UIButton {
    
    private lazy var im: CALayer = {
        let l = CALayer()
        l.contents = UIImage(named: "your_icon")?.cgImage
        l.contentsGravity = .resizeAspect
        layer.addSublayer(l)
        return l
    }()
    
    override func layoutSubviews() {
        super.layoutSubviews()
        im.frame = CGRect(origin: CGPoint(x: 2, y: 2),
             size: CGSize(width: 14, height: 14))
    }
}
Fattie
  • 27,874
  • 70
  • 431
  • 719
0

myView.layer.contents = UIImage(named: "star")?.cgImage

JonyFang
  • 5
  • 2
  • 3
    Please provide more details in your answer. Even if your answer is a complete solution, the asker will learn more if you take the time to explain what your code does, and how you came up with it. – Jolta Oct 30 '17 at 08:57
  • @Jolta I don't think there is any more details to add to OP's answer. This is the code. – Teng L Aug 23 '19 at 15:53
0

This should be a comment, but I lost few hours, so I'm making it a stand alone answer.

I was doing:

myView.layer.contents = UIImage(named: "star")

can you find what the problem is? Because contents is of type Any then this just compiles fine. But the correct code is:

myView.layer.contents = UIImage(named: "star").cgImage
mfaani
  • 33,269
  • 19
  • 164
  • 293