Removing all CALayer sublayers

I am having trouble removing all sublayers of a level. I am currently doing this manually, but this leads to unnecessary clutter. I found a lot of topics about this on 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 a separate NSArray, but the result was the same.

Any ideas?

Edit:

I thought this was working now, but I was wrong. It works fine with CALayers, but it does not work with CATextLayers. Any ideas?

+54
objective-c cocoa core-animation
Jan 14 '10 at 21:07
source share
14 answers

The easiest way to remove all sublayers from a level is to set the sublayer property to nil:

rootLayer.sublayers = nil;

+112
May 01 '10 at 2:03
source share

The following should work:

 for (CALayer *layer in [[rootLayer.sublayers copy] autorelease]) { [layer removeFromSuperlayer]; } 
+32
Jan 14 '10 at 21:11
source share
 [rootLayer.sublayers makeObjectsPerformSelector:@selector(removeFromSuperlayer)]; 
+16
Jan 18 '11 at 3:19
source share

Swift (short version):

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

+5
Jun 16 '16 at 15:14
source share

Call rootLayer.sublayers = nil; may crash (for example, if under iOS 8 you call removeFromSuperview twice on the rootLayer ).

The right way:

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

The copy call is necessary so that the array on which removeFromSuperlayer iteratively called does not change, otherwise an exception is thrown.

+4
Nov 17 '15 at 16:05
source share

How to use back enumeration?

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

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

+2
Jan 15
source share

Without disassembly, all sublayers cause unpleasant crashes in iOS 7, which can happen much later when the program is running. I checked this with rootLayer.sublayers = nil and [rootLayer.sublayers makeObjectsPerformSelector:@selector(removeFromSuperlayer)] . A system layer should be created that gets tangled.

You must save your own array of layers and delete them yourself:

 [myArrayOfLayersIAddedMyself makeObjectsPerformSelector:@selector(removeFromSuperlayer)]; 
+2
Jan 21 '14 at 1:07
source share

I tried to remove only the first sublevel with 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]; } 
+2
Jan 28 '14 at 18:06
source share

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 deleting the layers caused a crash). The approach that worked for me:

When adding 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 a UITableViewCell I created a copy of the SubLayers property and deleted everything that matches the name I assigned earlier:

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

Without crashing.

Hope this helps!

+1
Apr 15 '16 at 16:56
source share

Both self.view.layer.sublayers = nil and [layer removeFromSuperlayer] will fail if the UIView contains any kind of preview. The best way to remove CALayer from superLayer is to maintain an array of layers. For example, this array is arrayOfLayers,

Each time you add a layer to UIView.sublayer , add this layer to this array i.e. [

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

Removing only the call

 [arrayOfLayers makeObjectsPerformSelector:@selector(removeFromSuperlayer)]; 
0
Oct. 16 '15 at 11:02
source share

I ran into the same problem and found many solutions, but none of them are perfect. Finally, from the above pnavk answer , I got an idea. There is code for Xamarin / C # users.
The iOS version may be as follows:

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 code above will only work for the lower bound. You can change the border side according to your requirement.
Here, before adding any level to the controller, I just ran a for loop to check if any border is applied or not?
To identify a previously added border, I use the CALayer name property. And compare this layer before removing it from the sublayers.
I used the same code before using the name property, but it creates a random crash. But after using the name property and comparing the name before the deletion, the failure problem will be fixed.

I hope this helps someone.

0
Sep 12 '17 at 7:26
source share

Of course you can do:
self.layer.sublayers=nil;
as suggested by Pascal Bourke. But it is better to name the setter for the property of sublayers:
[self.layer setSublayers:nil];
To avoid cleaning problems, if possible.

-one
Apr 28 '15 at 19:30
source share

For swift3 + you can use this.

yourView.layer.sublayers?.removeAll(keepingCapacity: true)

See here: Apple API Reference

Or you can: yourView.layer.sublayers?.forEach{ $0.removeFromSuperlayer()}

-one
Nov 25 '16 at 7:32
source share

What to do:

 rootLayer.sublayers = @[]; 
-one
Mar 27 '17 at 20:55
source share



All Articles