iOS Core Animation: CALayer bringSublayerToFront?

30,896

Solution 1

I don't think such methods exist, but it's easy to roll your own.

// CALayer+Additions.h

@interface CALayer (Additions)
- (void)bringSublayerToFront:(CALayer *)layer;
- (void)sendSublayerToBack:(CALayer *)layer;
@end

// CALayer+Additions.m

@implementation CALayer (Additions)

- (void)bringSublayerToFront:(CALayer *)layer {
    CALayer *superlayer = self.superlayer;
    [self removeFromSuperlayer];
    [superlayer insertSublayer:gradientLayer atIndex:[self.sublayers count]-1];
}

- (void)sendSublayerToBack:(CALayer *)layer {
    CALayer *superlayer = self.superlayer;
    [self removeFromSuperlayer];
    [superlayer insertSublayer:gradientLayer atIndex:0];
}

Solution 2

I'm curious why none of these answers mention the zPosition attribute on CALayer. Core Animation looks at this attribute to figure out layer rendering order. The higher the value, the closer it is to the front. These answers all work as long as your zPosition is 0, but to easily bring a layer to the front, set its zPosition value higher than all other sublayers.

Solution 3

This is variation of @MattDiPasquale's implementation which reflects UIView's logic more precisely:

- (void) bringSublayerToFront:(CALayer *)layer
{
    [layer removeFromSuperlayer];
    [self insertSublayer:layer atIndex:[self.sublayers count]];
}

- (void) sendSublayerToBack:(CALayer *)layer
{
    [layer removeFromSuperlayer];
    [self insertSublayer:layer atIndex:0];
}

Note: if you don't use ARC, you may wish to add [layer retain] at top and [layer release] at bottom of both functions to make sure layer is not accidentally destructed in a case it has retain count = 1.

Solution 4

Swift 4 version.
Idea that layer itself has bringToFront and sendToBack methods.

#if os(iOS)
   import UIKit
#elseif os(OSX)
   import AppKit
#endif

extension CALayer {

   func bringToFront() {
      guard let sLayer = superlayer else {
         return
      }
      removeFromSuperlayer()
      sLayer.insertSublayer(self, at: UInt32(sLayer.sublayers?.count ?? 0))
   }

   func sendToBack() {
      guard let sLayer = superlayer else {
         return
      }
      removeFromSuperlayer()
      sLayer.insertSublayer(self, at: 0)
   }
}

Usage:

let view = NSView(frame: ...)
view.wantsLayer = true
view.layer?.backgroundColor = NSColor.gray.cgColor

let l1 = CALayer(...)
let l2 = CALayer(...)

view.layer?.addSublayer(l1)
view.layer?.addSublayer(l2)

l1.bringToFront()

Solution 5

Right code here

- (void)bringSublayerToFront:(CALayer *)layer {
    CALayer *superlayer = layer.superlayer;
    [layer removeFromSuperlayer];
    [superlayer insertSublayer:layer atIndex:[superlayer.sublayers count]];
}

- (void)sendSublayerToBack:(CALayer *)layer {
    CALayer *superlayer = layer.superlayer;
    [layer removeFromSuperlayer];
    [superlayer insertSublayer:layer atIndex:0];
}
Share:
30,896
ma11hew28
Author by

ma11hew28

Updated on November 19, 2020

Comments

  • ma11hew28
    ma11hew28 over 3 years

    How do I bring a CALayer sublayer to the front of all sublayers, analogous to -[UIView bringSubviewToFront]?

  • Klaas
    Klaas almost 12 years
    Any chance to get an explanation why someone has downvoted this?
  • sdsykes
    sdsykes almost 12 years
    Probably because the code is wrong. The argument layer is not used. gradientLayer is not defined. The superlayer call is not needed because the layer argument's superlayer is self. ivanzoid has posted corrected code.
  • walkingbrad
    walkingbrad almost 11 years
    the removeFromSuperlayer methods are unnecessary as it will be removed automatically when calling insertSublayer.
  • invalidname
    invalidname over 9 years
    Another problem with the removeFromSuperlayer: if layer is the only sublayer, removing it apparently causes the sublayers array to nil out. In Swift, this leads to an option-unwrapping crash on self.sublayers. (It may be OK on Obj-C, as [nil count] probably returns 0, which actually works).
  • Alex Reynolds
    Alex Reynolds over 7 years
    I totally forgot about zPosition and this answer is the best. Both answers work but this one is more simplistic and reliable IMO
  • spinacher
    spinacher about 5 years
    From the Apple documentation: "When inserting above or below another sublayer, you are only specifying the sublayer’s position in the sublayers array. The actual visibility of the layers is determined primarily by the value in their zPosition property and secondarily by their position in the sublayers array."
  • C0D3
    C0D3 about 2 years
    Except changing the zPosition isn't working for me for some reason. I'm trying to draw it on top of the TabBarController's TabBar and can't figure out why it won't bring it (draw) in front of it.