Alle Unteransichten entfernen?

316

Wenn meine App zu ihrem Root-View-Controller zurückkehrt, muss viewDidAppear:ich bei der Methode alle Unteransichten entfernen.

Wie kann ich das machen?

Ian Vink
quelle

Antworten:

569

Edit: Mit Dank an Cocoafan : Diese Situation wird durch die Tatsache durcheinander gebracht , dass NSViewund UIViewDinge anders zu behandeln. Für NSView(nur Desktop-Mac-Entwicklung) können Sie einfach Folgendes verwenden:

[someNSView setSubviews:[NSArray array]];

Für UIView(nur iOS-Entwicklung) können Sie sicher verwenden, makeObjectsPerformSelector:da die subviewsEigenschaft eine Kopie des Arrays von Unteransichten zurückgibt:

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

Vielen Dank an Tommy für den Hinweis, dass makeObjectsPerformSelector:das subviewsArray während der Aufzählung geändert zu werden scheint (wofür es funktioniert NSView, aber nicht für UIView).

Bitte lesen Sie diese SO-Frage für weitere Details.

Hinweis: Wenn Sie eine dieser beiden Methoden verwenden, werden alle in Ihrer Hauptansicht enthaltenen Ansichten entfernt und freigegeben , sofern sie nicht an anderer Stelle beibehalten werden. Aus der Apple-Dokumentation zu removeFromSuperview :

Wenn die Übersicht des Empfängers nicht Null ist, gibt diese Methode den Empfänger frei. Wenn Sie die Ansicht wiederverwenden möchten, müssen Sie sie beibehalten, bevor Sie diese Methode aufrufen, und sie nach Bedarf freigeben, wenn Sie damit fertig sind oder nachdem Sie sie einer anderen Ansichtshierarchie hinzugefügt haben.

James
quelle
9
Sind Sie sicher, dass dies sicher ist? Es mutiert die Liste, während es wiederholt wird, und ich kann keine endgültige Aussage in der Apple-Dokumentation finden.
Tommy
8
@ Tommy: Das ist ein guter Punkt. Einige Googler haben die Antwort aufgedeckt: Gibt UIVieweine Kopie des subviewsveränderlichen Arrays zurück, sodass dieser Code einfach funktioniert. Ganz andere Geschichte auf dem Desktop, bei der derselbe Code eine Ausnahme auslöst. Siehe stackoverflow.com/questions/4665179/…
e.James
3
UIView reagiert nicht auf setSubviews:, oder?
Cocoafan
4
die Xamarin-Methode: someUIView.Subviews.All (p => p.RemoveFromSuperview);
Benoit Jadinon
3
@BenoitJadinon - wird nicht kompiliert - Sie scheinen zu bedeuten, dass Sie All missbrauchen, um ForEach auszuführen someUIView.Subviews.All( v => { v.RemoveFromSuperview(); return true; } );. IMHO Reiniger zu sagen, was Sie meinen : someUIView.Subviews.ToList().ForEach( v => v.RemoveFromSuperview() );.
ToolmakerSteve
173

Holen Sie sich alle Unteransichten von Ihrem Root-Controller und senden Sie jeweils eine removeFromSuperview:

NSArray *viewsToRemove = [self.view subviews];
for (UIView *v in viewsToRemove) {
    [v removeFromSuperview];
}
Matthew McGoogan
quelle
+1 und danke. Ich hätte auch so verwenden sollen self.viewwie du.
e.James
2
warum nicht!? for (UIView *v in [self.view subviews])es ist einfacher
Frade
4
@Frade Es ist viel klarer und ausführlicher, wie er es getan hat. Ausführlich und lesbar> Tastatureingaben speichern
Taylorcressy
33
@taylorcressy Du hättest sagen sollen "Lesbarkeit ist wichtiger als Speichern von Tastenanschlägen" anstelle von "Lesbarkeit> Speichern von Tastenanschlägen" und dann wäre dein Kommentar besser lesbar. :-)
Arlomedia
1
Vergessen wir nicht die Tatsache, dass, wenn [self.view subviews] Berechnungen unter der Haube ausführt, das direkte Einfügen in die for-Schleife dazu führen kann, dass diese Berechnungen immer wieder ausgeführt werden. Wenn Sie es vor der Schleife deklarieren, wird sichergestellt, dass sie nur einmal ausgeführt werden.
Brian Sachetta
116

In Swift können Sie einen funktionalen Ansatz wie den folgenden verwenden:

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

Zum Vergleich würde der imperative Ansatz folgendermaßen aussehen:

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

Diese Codefragmente funktionieren jedoch nur unter iOS / tvOS . Unter macOS sieht das etwas anders aus.

Jeehut
quelle
4
(subviews as [UIView]).map { $0.removeFromSuperview() }
DeFrenZ
8
Es ist nicht funktionsfähig, da eine Funktion einen Wert zurückgibt und nur das Ergebnis von verwirft .map. Dies ist eine reine Nebenwirkung und wird besser so gehandhabt:view.subviews.forEach() { $0.removeFromSuperview() }
Martin Algesten
1
Du hast recht, Martin, ich stimme dir zu. Ich wusste nur nicht, dass es eine forEach () -Methode für Arrays gibt. Wann wurde es hinzugefügt oder habe ich es nur überwacht? Ich habe meine Antwort aktualisiert!
Jeehut
1
Ich bin so faul, dass ich, selbst wenn ich wusste, wie man Unteransichten löscht, immer noch hierher kam, um Ihr Snippet zu kopieren /
einzufügen
13

Wenn Sie alle Unteransichten in Ihrem UIView (hier yourView) entfernen möchten , schreiben Sie diesen Code auf Ihre Schaltfläche:

[[yourView subviews] makeObjectsPerformSelector: @selector(removeFromSuperview)];
Mohd Rahib
quelle
12
Willkommen bei Stack Overflow! Würden Sie eine Erzählung hinzufügen, um zu erklären, warum dieser Code funktioniert und was ihn zu einer Antwort auf die Frage macht? Dies wäre sehr hilfreich für die Person, die die Frage stellt, und für alle anderen, die mitkommen. Darüber hinaus enthält die bereits akzeptierte Antwort Code, der im Wesentlichen mit diesem identisch ist.
Andrew Barber
5
Wie könnte dies mehr helfen als die akzeptierte Antwort: Es ist identisch. Warum das schreiben?
Rambatino
8

Dies gilt nur für OSX, da unter iOS eine Kopie des Arrays aufbewahrt wird

Wenn Sie alle Unteransichten entfernen, empfiehlt es sich, am Ende des Arrays mit dem Löschen zu beginnen und das Löschen so lange fortzusetzen, bis Sie den Anfang erreicht haben. Dies kann mit diesen beiden Codezeilen erreicht werden:

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

oder (weniger effizient, aber besser lesbar)

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

HINWEIS

Sie sollten die Unteransichten NICHT in normaler Reihenfolge entfernen, da dies zu einem Absturz führen kann, wenn eine UIView-Instanz gelöscht wird, bevor die removeFromSuperviewNachricht an alle Objekte des Arrays gesendet wurde. (Das Löschen des letzten Elements würde natürlich keinen Absturz verursachen.)

Daher der Code

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

sollte NICHT verwendet werden.

Zitat aus der Apple-Dokumentation zu makeObjectsPerformSelector :

Sendet an jedes Objekt im Array die von einem bestimmten Selektor identifizierte Nachricht, beginnend mit dem ersten Objekt und weiter durch das Array bis zum letzten Objekt.

(was für diesen Zweck die falsche Richtung wäre)

Daniel
quelle
Können Sie bitte ein Beispiel dafür geben, worauf Sie sich beziehen? Sie wissen nicht, was Sie als "Element" bezeichnen. Und wie würden diese Elemente entfernt, bevor Sie removeFromSuperView aufrufen?
der Reverend
Aber wie kann eine Instanz von UIView beim Aufrufen dieser Methode gelöscht werden? Meinst du aus dem Subview-Array entfernt?
der Reverend
Wenn removeFromSuperviewder Vorgang abgeschlossen ist, wird die UIView aus dem Array entfernt. Wenn keine anderen lebenden Instanzen mit einer starken Beziehung zur UIView vorhanden sind, wird die UIView ebenfalls gelöscht. Dies kann zu einer Out-of-Bound-Ausnahme führen.
Daniel
1
Erwischt! Vielen Dank. Ich denke, Sie erhalten eine Kopie des Subviews-Arrays unter IOS. In jedem Fall ist es eine gute Idee, selbst eine Kopie zu erstellen, wenn Sie Unteransichten entfernen möchten.
der Reverend
@simpleBob - hast du die Kommentare von 2011 zu der akzeptierten Antwort gelesen? Laut diesen Kommentaren gibt iOS unter iOS [yourView subviews]eine KOPIE des Arrays zurück und ist daher sicher. (Beachten Sie, dass unter OSX das, was Sie sagen, korrekt ist.)
ToolmakerSteve
4

Versuchen Sie es auf diese Weise schnell 2.0

view.subviews.forEach { $0.removeFromSuperview() }
William Hu
quelle
2
Sehen Sie nicht das Datum der Antwort, an dem ich früher bin? Warum nicht meinen Antwortlink in diese Antwort einfügen?
William Hu
1
Richtig ... die Antwort wurde vor Ihrer veröffentlicht, aber die forEachbasierte Lösung wurde nach Ihrer hinzugefügt, das habe ich verpasst. Entschuldigung.
Cristik
4

Verwenden Sie den folgenden Code, um alle Unteransichten zu entfernen.

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

Verwenden der Swift- UIViewErweiterung:

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

Erstellen Sie in Ziel-C eine Kategoriemethode aus der UIView-Klasse.

- (void)removeAllSubviews
{
    for (UIView *subview in self.subviews)
        [subview removeFromSuperview];
}
Rickster
quelle
2
view.subviews.forEach { $0.removeFromSuperview() }
MAGiGO
quelle
2
Während dieser Code die Frage möglicherweise beantwortet, würde die Bereitstellung eines zusätzlichen Kontexts darüber, wie und / oder warum das Problem gelöst wird, den langfristigen Wert der Antwort verbessern.
Nic3500
1

So entfernen Sie alle Unteransichten Syntax:

- (void)makeObjectsPerformSelector:(SEL)aSelector;

Verwendungszweck :

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

Diese Methode ist in der Datei NSArray.h vorhanden und verwendet die NSArray-Schnittstelle (NSExtendedArray)

Jayprakash Dubey
quelle
1

Wenn Sie Swift verwenden, ist dies so einfach wie:

subviews.map { $0.removeFromSuperview }

Es ist in der Philosophie dem makeObjectsPerformSelectorAnsatz ähnlich , jedoch mit etwas mehr Typensicherheit.

lms
quelle
Dies ist semantisch falsch, mapsollte nicht zu Nebenwirkungen führen. Außerdem kann das gleiche Ergebnis über erzielt werden forEach.
Cristik
0

Für ios6 mit Autolayout musste ich ein wenig Code hinzufügen, um auch die Einschränkungen zu entfernen.

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

Ich bin mir sicher, dass es einen besseren Weg gibt, dies zu tun, aber es hat bei mir funktioniert. In meinem Fall konnte ich kein Direct verwenden, [tagview removeConstraints:tagview.constraints]da in XCode Einschränkungen festgelegt wurden, die gelöscht wurden.

Michael Anderson
quelle
0

In monotouch / xamarin.ios hat das bei mir funktioniert:

SomeParentUiView.Subviews.All(x => x.RemoveFromSuperview);
Daniele D.
quelle
-10

So entfernen Sie alle Unteransichten aus den Übersichten:

NSArray *oSubView = [self subviews];
for(int iCount = 0; iCount < [oSubView count]; iCount++)
{
    id object = [oSubView objectAtIndex:iCount];
    [object removeFromSuperview];
    iCount--;
}
Pravin
quelle
Ein paar große Fehler hier @Pravin. Zunächst müsste 'Objekt' als UIView * definiert werden, andernfalls würde ein Compilerfehler mit [Objekt removeFromSuperview] angezeigt. Zweitens dekrementiert Ihre for-Schleife iCount bereits, sodass Sie mit Ihrer iCount---Zeile eine zusätzliche überspringen. Und schließlich gibt es oben zwei funktionierende und korrekte Ansätze, und Ihr Ansatz ist weder eleganter noch schneller.
Amergin
5
jede Iteration Sie tun iCount++und iCount--den Index der gleiche verlassen, so dass es eine Endlosschleife , wenn sein wird [oSubView count]>0. Dies ist definitiv fehlerhaft und NICHT VERWENDBAR .
Daniel