Removing all CALayer's sublayers

Objective CCocoaCore Animation

Objective C Problem Overview


I have trouble with deleting all of layer's sublayers. I currently do this manually, but that brings unnecessary clutter. I found many topics about this in google, but no answer.

I tried to do something like this:

for(CALayer *layer in rootLayer.sublayers)
{
[layer removeFromSublayer];
}

but it didn't work.

Also, i tried to clone rootLayer.sublayers into separate NSArray, but result was the same.

Any ideas?

Edit:

I thought it works now, but I was wrong. It works good with CALayers, but it doesn't work with CATextLayers. Any ideas?

Objective C Solutions


Solution 1 - Objective C

The simplest way to remove all sublayers from a layer is to set the sublayer property to nil:

rootLayer.sublayers = nil;

Solution 2 - Objective C

The following should work:

for (CALayer *layer in [[rootLayer.sublayers copy] autorelease]) {
    [layer removeFromSuperlayer];
}

Solution 3 - Objective C

Swift (short version):

Solution 4 - Objective C

[rootLayer.sublayers makeObjectsPerformSelector:@selector(removeFromSuperlayer)];

Solution 5 - Objective C

Calling rootLayer.sublayers = nil; can cause a crash (e.g. if, under iOS 8, you call removeFromSuperview twice on the view owning rootLayer).

The right way should be:

[[rootLayer.sublayers copy] makeObjectsPerformSelector:@selector(removeFromSuperlayer)]

The call to copy is needed so that the array on which removeFromSuperlayer is iteratively called is not modified, otherwise an exception is raised.

Solution 6 - Objective C

Indiscriminately removing all sublayers causes nasty crashes in iOS 7, which can occur much later in the program's execution. I have tested this thoroughly using both rootLayer.sublayers = nil and [rootLayer.sublayers makeObjectsPerformSelector:@selector(removeFromSuperlayer)]. There must be a system-created layer that's getting messed up.

You have to keep your own array of layers and remove them yourself:

[myArrayOfLayersIAddedMyself makeObjectsPerformSelector:@selector(removeFromSuperlayer)];

Solution 7 - Objective C

Both setting self.view.layer.sublayers = nil and [layer removeFromSuperlayer] will result in crash if UIView contains any subview. Best way to remove CALayer from superLayer is by maintaining an array of layers. For instance that array is arrayOfLayers,

Everytime you add a layer on UIView.sublayer add that layer in this array...

For example

[arrayOfLayers addObject:layer];
[self.view.layer addSublayer:layer];

While removing just call this,

[arrayOfLayers makeObjectsPerformSelector:@selector(removeFromSuperlayer)];

Solution 8 - Objective C

I had to do this in Xamarin/C#. I had a UITableViewCell with some CAShapeLayers for borders. All of the above options (including copying the Array and then removing layers caused a crash). The approach that worked for me:

When adding the CALayer, I gave it a name:

	var border = new CALayer();
	border.BackgroundColor = color.CGColor;
	border.Frame = new  CGRect(x, y, width, height);
	border.Name = "borderLayerName";
    Layer.AddSublayer(border);

In PrepareForReuse on the UITableViewCell, I created a copy of the SubLayers property and removed anything that matched the name I assigned earlier:

	CALayer[] copy = new CALayer[Layer.Sublayers.Length];
	Layer.Sublayers.CopyTo(copy, 0);
	copy.FirstOrDefault(l => l.Name == "borderLayerName")?.RemoveFromSuperLayer();

No crashes.

Hope this helps!

Solution 9 - Objective C

You could simply provide an identifier for the CAlayer you have added and remove it by searching for it. Like so

 let spinLayer = CAShapeLayer()
 spinLayer.path = UIBezierPath()
 spinLayer.name = "spinLayer"


  func removeAnimation() {
     for layer in progressView.layer.sublayers ?? [CALayer]()
         where layer.name == "spinLayer" {
           layer.removeFromSuperlayer()
       }
   }

Solution 10 - Objective C

How about using reverse enumeration?

NSEnumerator *enumerator = [rootLayer.sublayers reverseObjectEnumerator];
for(CALayer *layer in enumerator) {
    [layer removeFromSuperlayer];
}

Because the group in sublayers are changed during enumeration, if the order is normal. I would like to know the above code's result.

Solution 11 - Objective C

I tried to delete just the first sublayer at index 0 and this worked:

- (UITableViewCell *)tableView:(UITableView *)tableView
         cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    if ([cell.contentView.layer.sublayers count] != 0) {
        [[cell.contentView.layer.sublayers objectAtIndex:0] removeFromSuperlayer];
    }

Solution 12 - Objective C

I have faced the same issue and found many solutions but none of them are perfect.
Finally from above answer of pnavk i got the idea. At there the code is for Xamarin/C# users.
The iOS version could be as below:

Swift 2.3

    if rootLayer.sublayers?.count > 0 {
        rootLayer.sublayers?.forEach {
            if $0.name == "bottomBorderLayer" {
                $0.removeFromSuperlayer()
            }
        }
    }
    
    let border = CALayer()
    let height = CGFloat(1.0)
    
    border.borderColor = UIColor.blackColor().CGColor
    border.borderWidth = height
    
    border.frame = CGRectMake(0, self.frame.size.height - height, self.frame.size.width, self.frame.size.height)
    
    border.name = "bottomBorderLayer"
    rootLayer.addSublayer(border)
    rootLayer.masksToBounds = true


Objective-C

if (rootLayer.sublayers.count > 0) {
    for (CALayer *layer in rootLayer.sublayers) {
        if ([layer.name isEqualToString:@"bottomBorderLayer"]) {
            [layer removeFromSuperlayer];
        }
    }
}

CALayer *border = [[CALayer alloc] init];
CGFloat height = 1.0;

border.borderColor = [UIColor blackColor].CGColor;
border.borderWidth = height;

border.frame = CGRectMake(0, self.view.frame.size.height - height, self.view.frame.size.width, self.view.frame.size.height);
border.name = @"bottomBorderLayer";
[rootLayer addSublayer:border];
rootLayer.masksToBounds = TRUE;

The above code will work for bottom border only. You can change the border side as per your requirement.
Here before adding any layer to the controller, I have just run a for loop to check whether any border is already applied or not?
To identify previously added border I use name property of CALayer. And compare that layer before remove from sublayers.
I have tried the same code before using name property, but It creates random crash. But after using name property and comparing name before remove will solve the crash issue.

I hope this will help someone.

Solution 13 - Objective C

For swift3+,You can use this.

See here: Apple API Reference

Or you can:

yourView.layer.sublayers?.forEach{ $0.removeFromSuperlayer()}

Solution 14 - Objective C

What about doing:

rootLayer.sublayers = @[];

Solution 15 - Objective C

For sure you can do:
self.layer.sublayers=nil;
as suggested by Pascal Bourque. But it's better to call the setter for sublayers property:
[self.layer setSublayers:nil];
To avoid any clean up issues if there might be.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionradexView Question on Stackoverflow
Solution 1 - Objective CPascal BourqueView Answer on Stackoverflow
Solution 2 - Objective CBen GottliebView Answer on Stackoverflow
Solution 3 - Objective Caverage JoeView Answer on Stackoverflow
Solution 4 - Objective CTomHView Answer on Stackoverflow
Solution 5 - Objective CKafuView Answer on Stackoverflow
Solution 6 - Objective CNestorView Answer on Stackoverflow
Solution 7 - Objective CAmit LimjeView Answer on Stackoverflow
Solution 8 - Objective CpnavkView Answer on Stackoverflow
Solution 9 - Objective Csavie elikemView Answer on Stackoverflow
Solution 10 - Objective CKatokichiSoftView Answer on Stackoverflow
Solution 11 - Objective Czero3nnaView Answer on Stackoverflow
Solution 12 - Objective CEr. ViharView Answer on Stackoverflow
Solution 13 - Objective CNeuneedView Answer on Stackoverflow
Solution 14 - Objective CJanView Answer on Stackoverflow
Solution 15 - Objective Cus_davidView Answer on Stackoverflow