Usunąć wszystkie widoki podrzędne?


316

Gdy moja aplikacja wróci do kontrolera widoku głównego, w viewDidAppear:metodzie muszę usunąć wszystkie widoki podrzędne.

W jaki sposób mogę to zrobić?

Odpowiedzi:


569

Edit: Z podziękowaniami dla cocoafan : Ta sytuacja jest zagmatwane przez fakt, że NSViewi UIViewrzeczy rękojeść inaczej. W przypadku NSView(tylko programowanie na komputery Mac) możesz po prostu użyć następujących opcji:

[someNSView setSubviews:[NSArray array]];

W przypadku UIView(tylko programowanie na iOS) można bezpiecznie korzystać, makeObjectsPerformSelector:ponieważ subviewswłaściwość zwróci kopię tablicy podrzędnych widoków:

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

Dziękujemy Tommy'emu za wskazanie, że makeObjectsPerformSelector:wygląda na to, że modyfikuje subviewstablicę podczas jej wyliczania (co robi dla NSView, ale nie dla UIView).

Zobacz to SO pytanie, aby uzyskać więcej informacji.

Uwaga: użycie jednej z tych dwóch metod usunie każdy widok, który zawiera widok główny, i zwolni je , jeśli nie zostaną zachowane w innym miejscu. Z dokumentacji Apple na removeFromSuperview :

Jeśli nadzór odbiornika nie jest zerowy, ta metoda zwalnia odbiorcę. Jeśli planujesz ponownie użyć widoku, pamiętaj o jego zachowaniu przed wywołaniem tej metody i pamiętaj, aby odpowiednio go zwolnić po zakończeniu pracy lub po dodaniu go do innej hierarchii widoków.


9
Czy na pewno jest to bezpieczne? Powoduje to mutację listy podczas iteracji i nie mogę znaleźć ostatecznego stwierdzenia w dokumentacji Apple.
Tommy

8
@Tommy: To dobra uwaga. Niektóre Googling pojawiła się odpowiedź: UIViewZwraca kopiowania na subviewszmienny tablicy, więc ten kod po prostu działa. Zupełnie inna historia na pulpicie, gdzie ten sam kod zgłosi wyjątek. Zobacz stackoverflow.com/questions/4665179/…
e.James

3
UIView nie odpowiada na setSubviews :, prawda?
cocoafan

4
sposób Xamarin: someUIView.Subviews.All (p => p.RemoveFromSuperview);
Benoit Jadinon

3
@BenoitJadinon - nie skompiluje się - wydaje się, że masz na myśli nadużywanie All do wykonywania ForEach, więc someUIView.Subviews.All( v => { v.RemoveFromSuperview(); return true; } );. IMHO czystsze powiedzieć, co to znaczy: someUIView.Subviews.ToList().ForEach( v => v.RemoveFromSuperview() );.
ToolmakerSteve,

173

Pobierz wszystkie widoki podrzędne z kontrolera głównego i wyślij każdemu removeFromSuperview:

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

+1 i dziękuję. Powinienem był również użyć self.viewtak jak ty.
e.James

2
Dlaczego nie!? for (UIView *v in [self.view subviews])jest łatwiej
Frade

4
@Frade Sposób, w jaki to zrobił, jest znacznie bardziej zrozumiały i bardziej szczegółowy. Pełne i czytelne> zapisywanie naciśnięć klawiszy
taylorcressy

33
@taylorcressy Powinieneś był powiedzieć, że „czytelność jest ważniejsza niż zapisywanie naciśnięć klawiszy” zamiast „czytelność> zapisywanie naciśnięć klawiszy”, a wtedy twój komentarz byłby bardziej czytelny. :-)
arlomedia

1
Nie zapominajmy o tym, że jeśli [podglądy self.view] wykona jakiekolwiek obliczenia pod maską, umieszczenie go bezpośrednio w pętli for może spowodować, że te obliczenia będą wykonywane w kółko. Deklaracja przed pętlą gwarantuje, że zostaną wykonane tylko raz.
Brian Sachetta

116

W Swift możesz zastosować takie funkcjonalne podejście :

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

Dla porównania, imperatywne podejście wyglądałoby tak:

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

Te fragmenty kodu działają jednak tylko w systemie iOS / tvOS , w systemie macOS sytuacja wygląda nieco inaczej.


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

8
nie działa, ponieważ funkcja zwraca wartość, a to po prostu odrzuca wynik .map. jest to czysty efekt uboczny i lepiej sobie z nim view.subviews.forEach() { $0.removeFromSuperview() }
radzić w następujący sposób

1
Masz rację, Martin, zgadzam się z tobą. Po prostu nie wiedziałem, że istnieje metoda forEach () na tablicach. Kiedy został dodany, czy właśnie go nadzorowałem? Zaktualizowałem swoją odpowiedź!
Jeehut,

1
Jestem tak leniwy, że nawet gdybym wiedział, jak wyczyścić podview, wciąż tu przyszedłem, aby skopiować / wkleić swój fragment
kodu

13

Jeśli chcesz usunąć wszystkie widoki podrzędne na swoim UIView (tutaj yourView), to napisz ten kod na kliknięcie przycisku:

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

12
Witamy w Stack Overflow! Czy zastanowiłbyś się nad dodaniem narracji wyjaśniającej, dlaczego ten kod działa i co czyni go odpowiedzią na pytanie? Byłoby to bardzo pomocne dla osoby zadającej pytanie i każdej innej, która się pojawi. Ponadto już zaakceptowana odpowiedź zawiera kod, który jest zasadniczo taki sam jak ten.
Andrew Barber

5
Jak może to pomóc bardziej niż przyjęta odpowiedź: Jest identyczna. Po co to pisać?
Rambatino

8

Dotyczy to tylko OSX, ponieważ w iOS przechowywana jest kopia tablicy

Podczas usuwania wszystkich widoków podrzędnych dobrym pomysłem jest rozpoczęcie usuwania na końcu tablicy i kontynuowanie usuwania aż do początku. Można to osiągnąć za pomocą dwóch wierszy kodu:

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();
}

lub (mniej wydajny, ale bardziej czytelny)

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

UWAGA

NIE powinieneś usuwać widoków podrzędnych w normalnej kolejności, ponieważ może to spowodować awarię, jeśli instancja UIView zostanie usunięta przed removeFromSuperviewwysłaniem wiadomości do wszystkich obiektów tablicy. (Oczywiście usunięcie ostatniego elementu nie spowoduje awarii)

Dlatego kod

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

NIE powinien być używany.

Cytat z dokumentacji Apple na temat makeObjectsPerformSelector :

Wysyła do każdego obiektu w tablicy komunikat zidentyfikowany przez dany selektor, zaczynając od pierwszego obiektu i kontynuując przez tablicę do ostatniego obiektu.

(co byłoby niewłaściwym kierunkiem do tego celu)


Czy możesz podać przykład tego, o czym mówisz? Nie wiem, co nazywacie „elementem”. Jak te elementy można usunąć przed wywołaniem metody removeFromSuperView?
Wielebny

Ale w jaki sposób można usunąć instancję UIView podczas wywoływania tej metody? Masz na myśli usunięcie z tablicy widoków podrzędnych?
Wielebny

Po removeFromSuperviewzakończeniu UIView zostanie usunięty z tablicy, a jeśli nie będzie innych żywych instancji silnie powiązanych z UIView, UIView również zostanie usunięty. Może to spowodować wyjątek spoza zakresu.
Daniel

1
Gotcha! Dziękuję Ci. Wydaje mi się, że dostajesz kopię tablicy subviews na IOS. W każdym razie dobrym pomysłem byłoby zrobienie kopii samodzielnie, jeśli chcesz usunąć podview.
Wielebny

@simpleBob - czy czytałeś komentarze napisane w 2011 r. do zaakceptowanej odpowiedzi? Zgodnie z tymi komentarzami na iOS [yourView subviews]zwraca COPY tablicy, dlatego jest bezpieczny. (UWAGA, że w OSX to, co mówisz, jest poprawne.)
ToolmakerSteve,

4

Wypróbuj w ten sposób swift 2.0

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

2
Nie widzisz wcześniejszej daty odpowiedzi? Dlaczego nie wkleić mojego linku odpowiedzi w tej odpowiedzi?
William Hu

1
Racja ... odpowiedź została wysłana przed tobą, jednak forEachrozwiązanie bazowe zostało dodane po twoim, przegapiłem to. Przeprosiny.
Cristik

4

Użyj następującego kodu, aby usunąć wszystkie widoki podrzędne.

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

2

Korzystanie z UIViewrozszerzenia Swift :

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

2

W celu C idź dalej i stwórz metodę kategorii poza klasą UIView.

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

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

2
Chociaż ten kod może odpowiedzieć na pytanie, zapewnienie dodatkowego kontekstu dotyczącego tego, jak i / lub dlaczego rozwiązuje problem, poprawiłoby długoterminową wartość odpowiedzi.
Nic3500,

1

W celu usunięcia wszystkich widoków podrzędnych Składnia:

- (void)makeObjectsPerformSelector:(SEL)aSelector;

Stosowanie :

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

Ta metoda jest obecna w pliku NSArray.h i wykorzystuje interfejs NSArray (NSExtendedArray)


1

Jeśli używasz Swift, jest to tak proste, jak:

subviews.map { $0.removeFromSuperview }

W filozofii jest podobny do makeObjectsPerformSelectorpodejścia, jednak z nieco większym bezpieczeństwem typu.


Jest to semantycznie nieprawidłowe, mapnie powinno powodować skutków ubocznych. Ponadto ten sam wynik można osiągnąć za pomocą forEach.
Cristik

0

W przypadku ios6 korzystającego z autolayout musiałem dodać trochę kodu, aby usunąć ograniczenia.

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)];

Jestem pewien, że jest to lepszy sposób, ale zadziałało to dla mnie. W moim przypadku nie mogłem użyć bezpośredniego, [tagview removeConstraints:tagview.constraints]ponieważ w XCode były ustawione ograniczenia, które były usuwane.


0

W monotouch / xamarin.ios zadziałało to dla mnie:

SomeParentUiView.Subviews.All(x => x.RemoveFromSuperview);

-10

Aby usunąć wszystkie podviewy z superviews:

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

Kilka głównych błędów tutaj @Pravin. Po pierwsze, trzeba zdefiniować „obiekt” jako UIView *, w przeciwnym razie wystąpi błąd kompilatora z [object removeFromSuperview]. Po drugie, twoja pętla for już zmniejsza wartość iCount, więc pomijasz dodatkowy w linii iCount. I na koniec, są dwa działające i poprawne podejścia powyżej, a twoje nie jest ani bardziej eleganckie, ani szybsze.
amergin

5
każdą iterację, którą robisz iCount++i iCount--pozostawiając ten sam indeks, więc będzie to nieskończona pętla jeśli [oSubView count]>0. Jest to zdecydowanie błąd i NIE MOŻNA użyć kodu.
Daniel
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.