Как удалить все подпредставления?

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

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


person Ian Vink    schedule 28.01.2010    source источник


Ответы (15)


Изменить: Благодаря cocoafan: эта ситуация запутана тем фактом, что NSView и UIView обращаться с вещами по-другому. Для NSView (только для настольных компьютеров Mac) вы можете просто использовать следующее:

[someNSView setSubviews:[NSArray array]];

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

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

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

Дополнительные сведения см. В этом вопросе SO.

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

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

person e.James    schedule 28.01.2010
comment
Вы уверены, что это безопасно? Он изменяет список при его повторении, и я не могу найти окончательного утверждения в документации Apple. - person Tommy; 08.03.2011
comment
@Tommy: Это хороший момент. Некоторые поисковые запросы в Google нашли ответ: UIView возвращает копию subviews изменяемого массива, так что этот код просто работает. Совершенно другая история на рабочем столе, где тот же код выдает исключение. См. stackoverflow.com / questions / 4665179 / - person e.James; 08.03.2011
comment
@Tommy: Я соответственно обновил свой ответ. Спасибо, что поймали это! - person e.James; 08.03.2011
comment
UIView не отвечает на setSubviews :, не так ли? - person cocoafan; 16.03.2011
comment
@cocoafan: Похоже, вы правы. Подпредставления можно установить только из подклассов UIView. Это NSView, что позволяет устанавливать их извне. - person e.James; 16.03.2011
comment
Не удалось найти способ сделать это в Swift - person Van Du Tran; 11.04.2015
comment
способ Xamarin: someUIView.Subviews.All (p = ›p.RemoveFromSuperview); - person Benoit Jadinon; 02.06.2015
comment
Хороший ответ, немного другой способ: создать категорию UIView и реализовать некоторый метод - (void) removeSubviews. Тот же механизм, но более красивый код. - person AzaFromKaza; 24.12.2015
comment
@BenoitJadinon - не компилируется - похоже, вы имеете в виду злоупотребление All для выполнения ForEach, поэтому someUIView.Subviews.All( v => { v.RemoveFromSuperview(); return true; } );. ИМХО чище сказать то, что вы имеете в виду: someUIView.Subviews.ToList().ForEach( v => v.RemoveFromSuperview() );. - person ToolmakerSteve; 13.07.2016

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

NSArray *viewsToRemove = [self.view subviews];
for (UIView *v in viewsToRemove) {
    [v removeFromSuperview];
}
person Matthew McGoogan    schedule 28.01.2010
comment
+1 и спасибо. Я должен был также использовать self.view, как и вы. - person e.James; 28.01.2010
comment
Почему нет!? for (UIView *v in [self.view subviews]) это проще - person Frade; 18.06.2014
comment
@Frade Он сделал это намного яснее и многословнее. Подробная информация и удобочитаемость ›сохранение нажатий клавиш - person taylorcressy; 21.08.2014
comment
@taylorcressy Вы должны были сказать, что удобочитаемость важнее, чем сохранение нажатий клавиш, а не удобочитаемость ›сохранение нажатий клавиш, и тогда ваш комментарий будет более читабельным. :-) - person arlomedia; 14.01.2015
comment
Не будем забывать о том факте, что если [self.view subviews] выполняет какие-либо вычисления под капотом, помещение его непосредственно в цикл for может привести к тому, что эти вычисления будут выполняться снова и снова. Объявление его перед циклом гарантирует, что они будут выполнены только один раз. - person Brian Sachetta; 17.07.2018

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

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

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

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

Эти фрагменты кода работают только в iOS / tvOS, однако в macOS все немного по-другому.

person Jeehut    schedule 25.11.2014
comment
он не работает, так как функция возвращает значение, а это просто отбрасывает результат .map. это чистый побочный эффект, и с ним лучше справиться следующим образом: view.subviews.forEach() { $0.removeFromSuperview() } - person Martin Algesten; 24.11.2015
comment
Ты прав, Мартин, я с тобой согласен. Я просто не знал, что в массивах есть метод forEach (). Когда это было добавлено или я просто следил за ним? Я обновил свой ответ! - person Jeehut; 25.11.2015
comment
Я настолько ленив, что даже если бы я знал, как очищать подпредставления, я все равно пришел сюда, чтобы скопировать / вставить ваш фрагмент и поставить +1 - person Vilmir; 04.08.2019

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

[[yourView subviews] makeObjectsPerformSelector: @selector(removeFromSuperview)];
person Mohd Rahib    schedule 03.05.2013
comment
Добро пожаловать в Stack Overflow! Не могли бы вы добавить немного повествования, чтобы объяснить, почему этот код работает, и что делает его ответом на вопрос? Это будет очень полезно для человека, задающего вопрос, и для всех, кто придет с ним. Кроме того, уже принятый ответ включает в себя код, который по сути совпадает с этим. - person Andrew Barber; 05.05.2013
comment
Как это могло помочь больше, чем принятый ответ: он идентичен. Зачем это писать? - person Rambatino; 29.05.2014

Это применимо только к 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:

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

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

person Daniel    schedule 22.05.2014
comment
Не могли бы вы привести пример того, о чем вы говорите? Не знаю, что вы называете элементом И как удалить эти элементы перед вызовом removeFromSuperView? - person the Reverend; 27.05.2015
comment
Но как можно удалить экземпляр UIView при вызове этого метода? Вы имеете в виду удаление из массива subview? - person the Reverend; 03.06.2015
comment
Когда removeFromSuperview завершится, UIView будет удален из массива, и если нет других живых экземпляров с сильной связью с UIView, UIView также будет удален. Это может вызвать исключение за пределами допустимого диапазона. - person Daniel; 03.06.2015
comment
Попался! Спасибо. Я думаю, вы получаете копию массива subviews на IOS. В любом случае было бы неплохо сделать копию самостоятельно, если вы хотите удалить подпредставления. - person the Reverend; 03.06.2015
comment
@simpleBob - вы читали комментарии, написанные в 2011 году по принятому ответу? Согласно этим комментариям, в iOS [yourView subviews] возвращает КОПИЮ массива, поэтому это безопасно. (ОБРАТИТЕ ВНИМАНИЕ, что в OS X то, что вы говорите, верно.) - person ToolmakerSteve; 13.07.2016
comment
Невозможно использовать изменяющийся член для неизменяемого значения: subviews - свойство только для получения - person JBarros35; 10.07.2021

Попробуйте так быстро 2.0

view.subviews.forEach { $0.removeFromSuperview() }
person William Hu    schedule 18.11.2015
comment
Разве вы не видите дату, когда я отвечу раньше? Почему бы не вставить ссылку на мой ответ в этот ответ? - person William Hu; 27.01.2016
comment
Правильно ... ответ был опубликован раньше вашего, однако решение на основе forEach было добавлено после вашего, я это пропустил. Извинения. - person Cristik; 27.01.2016

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

for (UIView *view in [self.view subviews]) 
{
 [view removeFromSuperview];
}
person Shahzaib Maqbool    schedule 19.03.2017

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

extension UIView {
    func removeAllSubviews() {
        for subview in subviews {
            subview.removeFromSuperview()
        }
    }
}
person mixel    schedule 08.09.2015

В objective-C создайте метод категории из класса UIView.

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

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

- (void)makeObjectsPerformSelector:(SEL)aSelector;

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

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

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

person Jayprakash Dubey    schedule 05.02.2014

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

subviews.map { $0.removeFromSuperview }

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

person lms    schedule 29.05.2015
comment
Это семантически неверно, map не должно вызывать побочных эффектов. К тому же такого же результата можно добиться с помощью forEach. - person Cristik; 26.10.2018

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

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 были установлены ограничения, которые очищались.

person Michael Anderson    schedule 31.07.2013

В monotouch / xamarin.ios это сработало для меня:

SomeParentUiView.Subviews.All(x => x.RemoveFromSuperview);
person Daniele D.    schedule 19.10.2016

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

NSArray *oSubView = [self subviews];
for(int iCount = 0; iCount < [oSubView count]; iCount++)
{
    id object = [oSubView objectAtIndex:iCount];
    [object removeFromSuperview];
    iCount--;
}
person Pravin    schedule 24.02.2014
comment
Пара серьезных ошибок здесь @Pravin. Во-первых, вам нужно, чтобы 'объект' был определен как UIView *, иначе вы получите ошибку компилятора с [object removeFromSuperview]. Во-вторых, ваш цикл for уже уменьшает iCount, поэтому вы пропускаете лишний со своей строкой iCount--. И, наконец, есть два рабочих и правильных подхода, описанных выше, и ваш не изящнее и не быстрее. - person amergin; 26.04.2014
comment
каждую итерацию вы выполняете iCount++ и iCount--, оставляя индекс одинаковым, поэтому цикл будет бесконечным, если [oSubView count]>0. Это определенно ошибочный и НЕИСПОЛЬЗУЕМЫЙ код. - person Daniel; 22.05.2014

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