35

I am looking for a general way to be able to search for a unique CALayer in a hierarchy without having to remember where the layer is in a hierarchy (and use the sublayer: and superlayer: methods).

I know this is possible with UIViews (which makes flipping views easy) but is it possible for CALayer?

thank you in advance for your help

Peyman

Peyman
  • 1,058
  • 1
  • 14
  • 27

5 Answers5

61

You can also use the name property of the CALayer.

[layer setName:@"myKey"];

To look it up,

- (CALayer *)myLayer {

    for (CALayer *layer in [superLayerOfMyLayer sublayers]) {

            if ([[layer name] isEqualToString:LabelLayerName]) {
                return layer;
            }
    }

    return nil;
}
EmptyStack
  • 51,274
  • 23
  • 147
  • 178
Mshah2
  • 2,169
  • 2
  • 14
  • 4
23

Apologize. I was being a dunce. CALayer is a key-value coding compliant container so I can create arbitrary values (including tags) in any instance. To create a tag for instance we do:

[rootLayer setValue:[NSNumber numberWithInt:101] forKey:@"PFtag"];

thank you

Peyman
  • 1,058
  • 1
  • 14
  • 27
11

In Swift you can do this

let layer = CAShapeLayer()
layer.name = "\(index)"

Then to get the layers you can do this

for item in self.layer.sublayers! {
    if item.name == "\(index)" {
        item.removeFromSuperlayer()
        item.removeAllAnimations()
    }
}
Anirudha Mahale
  • 2,526
  • 3
  • 37
  • 57
  • It'd be more idiomatic these days to use a where clause on your loop, rather than the if statement: `for item in self.layer.sublayers! where item.name == "\(index)"`. Also probably preferable to not use force unwraps, but that's more subjective. – Sam Jul 05 '20 at 18:54
  • I prefer ```self.layer.sublayers?.filter({ $0.name == "xxx" }).forEach..``` – woheras Jul 28 '22 at 10:34
6

A modern safe take on this.

Assign a name to the layer:

layer.name = "MyLayer"

Then, filter through all sublayers searching for the name:

layer.sublayers?
    .filter { layer in return layer.name == "MyLayer" }
    .forEach { layer in
        layer.removeFromSuperlayer()
        layer.removeAllAnimations()
}
CodeBender
  • 35,668
  • 12
  • 125
  • 132
  • Nice solution! I think you can replace `layer in return layer.name == "MyLayer"` with `$0.name == "MyLayer"`. – koen Jan 01 '20 at 22:12
  • 1
    Thanks @koen That is definitely valid, but sometimes I do prefer the clarity of naming it over using the $0. However, both work fine & just comes down to developer preference / enforced style (if applicable) – CodeBender Jan 01 '20 at 23:16
3

First, you need to name your CALayer:

    let shadowLayer = CALayer()
    shadowLayer.name = "myname"

After, you just do a search by name:

    if let neuBlackLayer = layer.sublayers?.first(where: { $0.name == "myname" }) {
       // do something with your layer
    }

same with guard:

    guard let neuBlackLayer = layer.sublayers?.first(where: { $0.name == "myname" }) else { return }
    // do something with your layer

Added this answer to clarify other's answers:

  1. It is clear, no need to filter or loops.
  2. It does what asked in question: the way to find a unique layer (there is nothing about removing it from superlayers, etc. )
  3. It is written using Swift 5.1
Zaporozhchenko Oleksandr
  • 4,660
  • 3
  • 26
  • 48