Удаление всех подслоев CALayer - PullRequest
59 голосов
/ 15 января 2010

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

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

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

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

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

Есть идеи?

Edit:

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

Ответы [ 14 ]

125 голосов
/ 01 мая 2010

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

rootLayer.sublayers = nil;

33 голосов
/ 15 января 2010

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

for (CALayer *layer in [[rootLayer.sublayers copy] autorelease]) {
    [layer removeFromSuperlayer];
}
18 голосов
/ 18 января 2011
[rootLayer.sublayers makeObjectsPerformSelector:@selector(removeFromSuperlayer)];
11 голосов
/ 16 июня 2016

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

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

5 голосов
/ 17 ноября 2015

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

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

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

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

3 голосов
/ 16 октября 2015

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

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

Например

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

При удалении просто позвоните,

[arrayOfLayers makeObjectsPerformSelector:@selector(removeFromSuperlayer)];
3 голосов
/ 21 января 2014

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

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

[myArrayOfLayersIAddedMyself makeObjectsPerformSelector:@selector(removeFromSuperlayer)];
2 голосов
/ 28 января 2014

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

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

    if ([cell.contentView.layer.sublayers count] != 0) {
        [[cell.contentView.layer.sublayers objectAtIndex:0] removeFromSuperlayer];
    }
2 голосов
/ 15 января 2010

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

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

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

1 голос
/ 12 сентября 2017

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

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;

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

Надеюсь, это кому-нибудь поможет.

...