Видалити всі підперегляди?


316

Коли моя програма повертається до свого контролера кореневого виду, у viewDidAppear:способі мені потрібно видалити всі підпогляди.

Як я можу це зробити?

Відповіді:


569

Редагувати: Завдяки какаофану : Ця ситуація заплутана тим, що 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
@Tommy: Це хороший момент. Деякі Googling з'явився відповідь: UIViewповертає копію на subviewsзмінюваний масив, так що цей код просто працює. Зовсім інша історія на робочому столі, де той самий код викине виняток. Див stackoverflow.com/questions/4665179 / ...
e.James

3
UIView не відповідає setSubviews:, чи не так?
какаофан

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

Отримайте всі підпогляди від вашого кореневого контролера та надсилайте кожному видалення видаленняFromSuperview:

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])простіше
Фрейд

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
Vilmir

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 :

Відправляє кожному об'єкту в масиві повідомлення, ідентифіковане заданим селектором, починаючи з першого об’єкта і продовжуючи через масив до останнього об'єкта.

(що було б неправильним напрямом для цієї мети)


Чи можете ви надати приклад того, про що ви маєте на увазі? Не знаєте, про що ви посилаєтесь як "елемент". І як би їх видалити, перш ніж викликати deleteFromSuperView?
преподобний

Але як можна видалити екземпляр UIView під час виклику цього методу? Ви маєте на увазі, що вилучено з масиву субпрогляду?
Преосвященний

По removeFromSuperviewзавершенні UIView буде видалено з масиву, і якщо немає інших живих екземплярів, що мають сильне відношення до UIView, UIView також буде видалений. Це може спричинити позамежний виняток.
Даніель

1
Отримав! Дякую. Я думаю, що ви отримуєте копію масиву підпрезентацій на IOS. У будь-якому випадку, було б непогано зробити копію самостійно, якщо ви хочете видалити підгляди.
Преосвященний

@simpleBob - чи читали ви коментарі, написані у 2011 році щодо прийнятої відповіді? Згідно з цими коментарями, на iOS [yourView subviews]повертає КОПІЮВАННЯ масиву, тому це безпечно. (Зверніть увагу, що на OSX те, що ви говорите, правильно.)
ToolmakerSteve

4

Спробуйте таким чином швидко 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

У Objective-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. По-перше, вам потрібно буде визначити "об'єкт" як UIView *, інакше ви отримаєте помилку компілятора з [object removeFromSuperview]. По-друге, ваш цикл for вже скорочує iCount, тому ви пропускаєте додатковий рядок iCount--. І, нарешті, вище два робочих і правильних підходу, і ваш ні елегантніший, ні швидший.
амергін

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