Удаление всех Подслоев CALayer


У меня возникли проблемы с удалением всех подслоев слоя. В настоящее время я делаю это вручную, но это приносит ненужную. Я нашел много тем об этом в google, но ответа нет.

Я пытался сделать что-то вроде этого:

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

но это не сработало.

кроме того, я пытался клонировать rootLayer.подслои в отдельный NSArray, но результат был тот же.

какие идеи?

Edit:

Я думал, что это работает сейчас, но я неправый. Он хорошо работает с CALayers, но он не работает с CATextLayers. Есть идеи?

14 56

14 ответов:

самый простой способ удалить все подслои из слоя-установить свойство подслоя равным nil:

rootLayer.sublayers = nil;

следующее должно работать:

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

Swift (короткая версия):

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

вызов rootLayer.sublayers = nil; может вызвать сбой (например, если в iOS 8 вы дважды вызываете removeFromSuperview в представлении, владеющем rootLayer).

правильный путь должен быть:

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

вызов copy - это необходимо, чтобы массив, на котором removeFromSuperlayer многократно называют не изменяется, в противном случае возникает исключение.

Как насчет использования обратного перечисления?

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

потому что группы в подслоях изменяются во время перечисления, если порядок нормальный. Я хотел бы знать результат вышеуказанного кода.

без разбора удаление всех подслоев вызывает неприятные сбои в iOS 7, которые могут произойти гораздо позже при выполнении программы. Я проверил это тщательно, используя оба rootLayer.sublayers = nil и [rootLayer.sublayers makeObjectsPerformSelector:@selector(removeFromSuperlayer)]. Там должен быть системный слой, который запутывается.

вы должны сохранить свой собственный массив слоев и удалить их самостоятельно:

[myArrayOfLayersIAddedMyself makeObjectsPerformSelector:@selector(removeFromSuperlayer)];

Я попытался удалить только первый подслой с индексом 0, и это сработало:

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

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

Я должен был сделать это в Xamarin/C#для. У меня был UITableViewCell С CAShapeLayers за границы. Все вышеперечисленные параметры (включая копирование массива и последующее удаление слоев) вызвали сбой. Подход, который работал для меня:

при добавлении CALayer, Я дал ему имя:

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

на PrepareForReuse на UITableViewCell Я создал копию SubLayers свойство и удалил все, что соответствовало имени, которое я назначил ранее:

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

нет крахи.

надеюсь, что это помогает!

я столкнулся с той же проблемой и нашел много решений, но ни одно из них не является идеальным.
Наконец-то от над ответом pnavk у меня есть идея. Там код предназначен для пользователей Xamarin/C#.
Версия iOS может быть как ниже:

Swift 2.3

    if rootLayer.sublayers?.count > 0 {
        rootLayer.sublayers?.forEach {
            if .name == "bottomBorderLayer" {
                .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


С

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;

приведенный выше код будет работать только для нижней границы. Вы можете изменить сторону границы согласно вашему требование.
Здесь перед добавлением любого слоя к контроллеру я просто запускаю цикл for, чтобы проверить, применяется ли какая-либо граница или нет?
Для идентификации ранее добавленной границы я использую свойство name CALayer. И сравните этот слой перед удалением из подслоев.
Я пробовал тот же код перед использованием свойства name, но он создает случайный сбой. Но после использования свойства name и сравнения имени перед удалением решит проблему сбоя.

Я надеюсь, что это будет помогите кому-нибудь.

как параметр self.view.layer.sublayers = nil и [layer removeFromSuperlayer] приведет к аварии, если UIView содержит подпанели. Лучший способ удалить CALayer от суперслоя путем поддержания массива слоев. Например, что массив arrayOfLayers,

каждый раз, когда вы добавляете слой на UIView.sublayer добавьте этот слой в этот массив...

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

при удалении просто называем это

[arrayOfLayers makeObjectsPerformSelector:@selector(removeFromSuperlayer)];

конечно, вы можете сделать:
self.layer.sublayers=nil;
как предложил Паскаль Бурк. Но лучше вызвать сеттер для свойства подслоев:
[self.layer setSublayers:nil];
Чтобы избежать каких-либо проблем с очисткой, если они могут быть.

для swift3+,вы можете использовать это.

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

смотрите здесь: Apple API Reference

или вы можете: yourView.layer.sublayers?.forEach{ .removeFromSuperlayer()}

Что делать:

rootLayer.sublayers = @[];