Удалить все подпредставления?


316

Когда мое приложение возвращается к своему корневому контроллеру представления, в viewDidAppear:методе мне нужно удалить все подпредставления.

Как я могу это сделать?

Ответы:


569

Редактировать: С благодаря Cocoafan : Эта ситуация запутана тем, что NSViewи по- UIViewразному. Для NSView(только для разработки на настольном Mac) вы можете просто использовать следующее:

[someNSView setSubviews:[NSArray array]];

Для UIView(только для iOS) вы можете безопасно использовать, makeObjectsPerformSelector:потому что subviewsсвойство вернет копию массива подпредставлений:

[[someUIView subviews]
 makeObjectsPerformSelector:@selector(removeFromSuperview)];

Спасибо Томми за то, что он указал, что, по- makeObjectsPerformSelector:видимому, изменяет subviewsмассив во время его перечисления (что он делает для NSView, но не для UIView).

Пожалуйста, смотрите этот вопрос для получения более подробной информации.

Примечание. Использование любого из этих двух методов приведет к удалению всех представлений, содержащихся в вашем главном представлении, и их освобождению , если они не сохраняются в другом месте. Из документации Apple по removeFromSuperview :

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


9
Вы уверены, что это безопасно? Он изменяет список во время итерации, и я не могу найти однозначного утверждения в документации Apple.
Томми

8
@ Томми: Это хороший момент. Некоторые Googling появился ответ: UIViewвозвращает копию на subviewsизменяемый массив, так что этот код просто работает. Совершенно другая история на рабочем столе, где один и тот же код выдаст исключение. См stackoverflow.com/questions/4665179/...
e.James

3
UIView не отвечает на setSubviews :, не так ли?
cocoafan

4
способ Xamarin: someUIView.Subviews.All (p => p.RemoveFromSuperview);
Бенуа Джадинон

3
@BenoitJadinon - не скомпилируется - вы, похоже, имеете в виду злоупотребление All для выполнения ForEach, поэтому someUIView.Subviews.All( v => { v.RemoveFromSuperview(); return true; } );. ИМХО чище сказать что ты имеешь ввиду someUIView.Subviews.ToList().ForEach( v => v.RemoveFromSuperview() );.
ToolmakerSteve

173

Получите все подпредставления от вашего корневого контроллера и отправьте каждому a removeFromSuperview:

NSArray *viewsToRemove = [self.view subviews];
for (UIView *v in viewsToRemove) {
    [v removeFromSuperview];
}

+1 и спасибо. Я должен был также использовать self.viewкак ты.
e.James

2
почему нет!? for (UIView *v in [self.view subviews])это проще
Frade

4
@Frade Это намного понятнее и многословнее, как он это сделал. Подробный и читабельный> сохранение нажатий клавиш
taylorcressy

33
@taylorcressy Вы должны были сказать «удобочитаемость важнее, чем сохранение нажатий клавиш» вместо «читабельность> сохранение нажатий клавиш», и тогда ваш комментарий стал бы более читабельным. :-)
arlomedia

1
Давайте не будем забывать о том факте, что если [self.view subviews] выполнит какие-либо вычисления внутри системы, то помещение его непосредственно в цикл for может привести к тому, что эти вычисления будут выполняться снова и снова. Объявление его перед циклом гарантирует, что они выполняются только один раз.
Брайан Сашетта

116

В Swift вы можете использовать такой функциональный подход :

view.subviews.forEach { $0.removeFromSuperview() }

Для сравнения, императивный подход будет выглядеть так:

for subview in view.subviews {
    subview.removeFromSuperview()
}

Эти фрагменты кода работают только в iOS / tvOS, хотя в MacOS все немного иначе.


4
(subviews as [UIView]).map { $0.removeFromSuperview() }
DeFrenZ

8
это не функционально, так как функция возвращает значение, а это просто отбрасывает результат .map. это чистый побочный эффект, с которым лучше справляться следующим образом:view.subviews.forEach() { $0.removeFromSuperview() }
Мартин Альгестен,

1
Ты прав, Мартин, я согласен с тобой. Я просто не знал, что в массивах есть метод forEach (). Когда это было добавлено или я только наблюдал за этим? Я обновил свой ответ!
Jeehut

1
Я так ленив, что даже если бы я знал, как очистить подпредставления, я все же пришел сюда, чтобы скопировать / вставить ваш фрагмент и поставить +1
Вильмир

13

Если вы хотите удалить все подпредставления в вашем UIView (здесь yourView), то напишите этот код при нажатии кнопки:

[[yourView subviews] makeObjectsPerformSelector: @selector(removeFromSuperview)];

12
Добро пожаловать в стек переполнения! Не могли бы вы добавить описательную часть, чтобы объяснить, почему этот код работает, и что делает его ответом на вопрос? Это было бы очень полезно для человека, задающего вопрос, и для всех, кто придет. Кроме того, уже принятый ответ включает в себя код, который по сути такой же, как этот.
Эндрю Барбер

5
Как это могло помочь больше, чем принятый ответ: он идентичен. Зачем писать это?
Рамбатино

8

Это относится только к OSX, так как в iOS сохраняется копия массива

При удалении всех подпредставлений хорошей идеей будет начать удаление в конце массива и продолжать удаление, пока не дойдете до начала. Это может быть достигнуто с помощью этих двух строк кода:

for (int i=mySuperView.subviews.count-1; i>=0; i--)
        [[mySuperView.subviews objectAtIndex:i] removeFromSuperview];

SWIFT 1.2

for var i=mySuperView.subviews.count-1; i>=0; i-- {
    mySuperView.subviews[i].removeFromSuperview();
}

или (менее эффективно, но более читабельно)

for subview in mySuperView.subviews.reverse() {
    subview.removeFromSuperview()
}

НОТА

Вы НЕ должны удалять подпредставления в обычном порядке, так как это может вызвать сбой, если экземпляр UIView удаляется до того, как removeFromSuperviewсообщение было отправлено всем объектам массива. (Очевидно, что удаление последнего элемента не приведет к сбою)

Поэтому код

[[someUIView subviews] makeObjectsPerformSelector:@selector(removeFromSuperview)];

должны НЕ быть использованы.

Цитата из документации Apple о makeObjectsPerformSelector :

Посылает каждому объекту в массиве сообщение, идентифицированное данным селектором, начиная с первого объекта и продолжая через массив до последнего объекта.

(что было бы неправильным направлением для этой цели)


Можете ли вы привести пример того, что вы имеете в виду? Не знаете, что вы называете «элементом» И как эти элементы будут удалены перед вызовом removeFromSuperView?
Преподобный

Но как можно удалить экземпляр UIView при вызове этого метода? Вы имеете в виду удаленный из массива subview?
Преподобный

По removeFromSuperviewокончании UIView будет удален из массива, и если нет других живых экземпляров, имеющих сильное отношение к UIView, UIView также будет удален. Это может вызвать исключение из-за границы.
Даниэль

1
Попался! Спасибо. Я думаю, что вы получаете копию массива subviews на IOS. В любом случае, было бы неплохо сделать копию самостоятельно, если вы хотите удалить подпредставления.
Преподобный

@simpleBob - вы читали комментарии к принятому ответу, написанные в 2011 году? Согласно этим комментариям, на iOS [yourView subviews]возвращает копию массива, поэтому является безопасным. (ЗАМЕТЬТЕ, что на OSX, что вы говорите, правильно.)
ToolmakerSteve

4

Попробуйте этот способ Swift 2.0

view.subviews.forEach { $0.removeFromSuperview() }

2
Разве ты не видишь дату, дату ответа я раньше? Почему бы не вставить ссылку на мой ответ в этот ответ?
Уильям Ху

1
Правильно ... ответ был опубликован до вашего, однако forEachпосле вашего решения было добавлено основанное решение, я пропустил это. Извиняюсь.
Кристик

4

Используйте следующий код, чтобы удалить все подпредставления.

for (UIView *view in [self.view subviews]) 
{
 [view removeFromSuperview];
}

2

Использование UIViewрасширения Swift :

extension UIView {
    func removeAllSubviews() {
        for subview in subviews {
            subview.removeFromSuperview()
        }
    }
}

2

В target-C вы можете создать метод категории из класса UIView.

- (void)removeAllSubviews
{
    for (UIView *subview in self.subviews)
        [subview removeFromSuperview];
}

2
view.subviews.forEach { $0.removeFromSuperview() }

2
Хотя этот код может ответить на вопрос, предоставление дополнительного контекста относительно того, как и / или почему он решает проблему, улучшит долгосрочную ценность ответа.
Nic3500

1

Для того, чтобы удалить все подпункты синтаксиса:

- (void)makeObjectsPerformSelector:(SEL)aSelector;

Использование :

[self.View.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];

Этот метод присутствует в файле NSArray.h и использует интерфейс NSArray (NSExtendedArray)


1

Если вы используете Swift, это так просто, как:

subviews.map { $0.removeFromSuperview }

По своей философии это похоже на makeObjectsPerformSelectorподход, но с большей степенью безопасности.


Это семантически неверно, mapне должно приводить к побочным эффектам. Кроме того , тот же результат может быть достигнут с помощью forEach.
Кристик

0

Для ios6, использующего autolayout, мне пришлось добавить немного кода, чтобы снять ограничения.

NSMutableArray * constraints_to_remove = [ @[] mutableCopy] ;
for( NSLayoutConstraint * constraint in tagview.constraints) {
    if( [tagview.subviews containsObject:constraint.firstItem] ||
       [tagview.subviews containsObject:constraint.secondItem] ) {
        [constraints_to_remove addObject:constraint];
    }
}
[tagview removeConstraints:constraints_to_remove];

[ [tagview subviews] makeObjectsPerformSelector:@selector(removeFromSuperview)];

Я уверен, что есть лучший способ сделать это, но это сработало для меня. В моем случае я не мог использовать прямой, [tagview removeConstraints:tagview.constraints]поскольку в XCode были установлены ограничения, которые очищались.



-10

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

NSArray *oSubView = [self subviews];
for(int iCount = 0; iCount < [oSubView count]; iCount++)
{
    id object = [oSubView objectAtIndex:iCount];
    [object removeFromSuperview];
    iCount--;
}

Пара серьезных ошибок здесь @Pravin. Во-первых, вам нужно, чтобы 'object' был определен как UIView *, иначе вы получите ошибку компилятора с [object removeFromSuperview]. Во-вторых, ваш цикл for уже уменьшает iCount, поэтому вы пропускаете дополнительный цикл со своей строкой iCount--. И наконец, есть два рабочих и правильных подхода выше, и ваш не является ни более элегантным, ни быстрым.
Amergin

5
каждую итерацию вы делаете iCount++и iCount--, оставляя индекс одинаковым, так что это будет бесконечный цикл, если [oSubView count]>0. Это определенно глючит и НЕ ПОЛЕЗНЫЙ код.
Даниэль
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.