Rimuovere tutte le visualizzazioni secondarie?


316

Quando la mia app torna al suo controller di visualizzazione principale, nel viewDidAppear:metodo devo rimuovere tutte le visualizzazioni secondarie.

Come posso fare questo?

Risposte:


569

Edit: Si ringrazia cocoafan : Questa situazione è confusa dal fatto che NSViewe UIViewcose maniglia in modo diverso. Per NSView(solo sviluppo desktop Mac), puoi semplicemente utilizzare quanto segue:

[someNSView setSubviews:[NSArray array]];

Per UIView(solo sviluppo iOS), puoi tranquillamente utilizzarlo makeObjectsPerformSelector:perché la subviewsproprietà restituirà una copia dell'array di sottoview:

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

Grazie a Tommy per aver sottolineato che makeObjectsPerformSelector:sembra modificare l' subviewsarray mentre viene elencato (cosa che fa per NSView, ma non per UIView).

Si prega di consultare questa domanda SO per maggiori dettagli.

Nota: l' utilizzo di uno di questi due metodi rimuoverà tutte le viste contenute nella vista principale e le rilascerà , se non vengono conservate altrove. Dalla documentazione di Apple su removeFromSuperview :

Se la superview del ricevitore non è nulla, questo metodo rilascia il ricevitore. Se si prevede di riutilizzare la vista, assicurarsi di conservarla prima di chiamare questo metodo e di rilasciarla nel modo appropriato al termine o dopo averla aggiunta a un'altra gerarchia di viste.


9
Sei sicuro che sia sicuro? Muta l'elenco durante l'iterazione e non riesco a trovare una dichiarazione definitiva nella documentazione di Apple.
Tommy

8
@ Tommy: questo è un buon punto. Un po 'googling alzato la risposta: UIViewrestituisce una copia della subviewsmatrice mutevole, quindi questo codice funziona solo. Storia completamente diversa sul desktop, in cui lo stesso codice genererà un'eccezione. Vedere stackoverflow.com/questions/4665179/...
e.James

3
UIView non risponde a setSubviews: vero?
Cocoaan,

4
il modo Xamarin: someUIView.Subviews.All (p => p.RemoveFromSuperview);
Benoit Jadinon,

3
@BenoitJadinon - non compilare - sembra che tu voglia abusare di tutti per eseguire ForEach, quindi someUIView.Subviews.All( v => { v.RemoveFromSuperview(); return true; } );. IMHO più pulito per dire quello che vuoi dire: someUIView.Subviews.ToList().ForEach( v => v.RemoveFromSuperview() );.
ToolmakerSteve

173

Ricevi tutte le visualizzazioni secondarie dal tuo controller di root e invia a ognuna una rimozione

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

+1 e grazie. Avrei dovuto usare anche self.viewcome hai fatto tu.
e.James

2
perchè no!? for (UIView *v in [self.view subviews])è più facile
Frade,

4
@Frade È molto più chiaro e dettagliato il modo in cui lo ha fatto. Verbose e leggibilità> salvataggio dei tasti
taylorcressy

33
@taylorcressy Avresti dovuto dire "la leggibilità è più importante del salvataggio dei tasti" anziché "leggibilità> salvataggio dei tasti" e quindi il tuo commento sarebbe più leggibile. :-)
arlomedia,

1
Non dimentichiamoci del fatto che se [sottopagine self.view] esegue dei calcoli sotto il cofano, metterlo direttamente nel ciclo for potrebbe causare l'esecuzione ripetuta di tali calcoli. Dichiararlo prima del loop assicura che vengano eseguiti solo una volta.
Brian Sachetta,

116

In Swift puoi utilizzare un approccio funzionale come questo:

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

A titolo di confronto, l' approccio imperativo sarebbe simile al seguente:

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

Questi frammenti di codice funzionano solo su iOS / tvOS , tuttavia le cose sono leggermente diverse su macOS.


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

8
non è funzionale poiché una funzione restituisce un valore e questo scarta solo il risultato di .map. questo è un effetto collaterale puro ed è gestito meglio in questo modo:view.subviews.forEach() { $0.removeFromSuperview() }
Martin Algesten,

1
Hai ragione, Martin, sono d'accordo con te. Non sapevo che esistesse un metodo forEach () su Array. Quando è stato aggiunto o l'ho appena supervisionato? Ho aggiornato la mia risposta!
Jeehut,

1
Sono così pigro che anche se sapessi come cancellare le subview, sono ancora venuto qui per copiare / incollare il tuo frammento e mettere un +1
Vilmir

13

Se vuoi rimuovere tutte le visualizzazioni secondarie sul tuo UIView (qui yourView), quindi scrivi questo codice con il tuo pulsante fai clic:

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

12
Benvenuto in Stack Overflow! Considereresti di aggiungere un po 'di narrativa per spiegare perché questo codice funziona e cosa lo rende una risposta alla domanda? Ciò sarebbe molto utile per la persona che pone la domanda e per chiunque altro vi si presenti. Inoltre, la risposta già accettata include un codice sostanzialmente uguale a questo.
Andrew Barber,

5
In che modo questo potrebbe aiutare più della risposta accettata: è identico. Perché scrivere questo?
Rambatino,

8

Questo vale solo per OSX poiché in iOS viene conservata una copia dell'array

Quando si rimuovono tutte le visualizzazioni secondarie, è una buona idea iniziare a eliminare alla fine dell'array e continuare a eliminare fino a raggiungere l'inizio. Questo può essere realizzato con queste due righe di codice:

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

o (meno efficiente, ma più leggibile)

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

NOTA

NON è necessario rimuovere le visualizzazioni secondarie nell'ordine normale, poiché potrebbe causare un arresto anomalo se un'istanza di UIView viene eliminata prima diremoveFromSuperview messaggio sia stato inviato a tutti gli oggetti dell'array. (Ovviamente, l'eliminazione dell'ultimo elemento non causerebbe un arresto anomalo)

Pertanto, il codice

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

NON dovrebbe essere usato.

Citazione dalla documentazione Apple su makeObjectsPerformSelector :

Invia a ciascun oggetto dell'array il messaggio identificato da un determinato selettore, iniziando dal primo oggetto e proseguendo attraverso l'array fino all'ultimo oggetto.

(che sarebbe la direzione sbagliata per questo scopo)


Potete per favore fare un esempio di ciò a cui vi riferite? Non so a cosa ti riferisci come "elemento" E come verrebbero rimossi questi elementi prima di chiamare removeFromSuperView?
il reverendo

Ma come si può eliminare un'istanza di UIView mentre si chiama questo metodo? Vuoi dire rimosso dall'array subview?
il reverendo

Al removeFromSuperviewtermine, l'UIView verrà rimosso dall'array e, se non vi sono altre istanze viventi con una forte relazione con l'UIView, anche l'UIView verrà eliminato. Ciò può causare un'eccezione fuori limite.
Daniel

1
Gotcha! Grazie. Penso che stai ricevendo una copia dell'array di subview su IOS. In ogni caso, sarebbe una buona idea crearne una copia se si desidera rimuovere le visualizzazioni secondarie.
il reverendo

@simpleBob - hai letto i commenti scritti nel 2011 sulla risposta accettata? Secondo questi commenti, su iOS, [yourView subviews]restituisce una COPIA dell'array, quindi è sicuro. (NOTA che su OSX, ciò che dici è corretto.)
ToolmakerSteve

4

Prova in questo modo swift 2.0

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

2
Non vedi la data di risposta della data che sono prima? Perché non incollare il mio link di risposta in quella risposta?
William Hu,

1
Giusto ... la risposta è stata pubblicata prima della tua, ma la forEachsoluzione di base è stata aggiunta dopo la tua, l'ho persa. Scuse.
Cristik,

4

Utilizzare il codice seguente per rimuovere tutte le visualizzazioni secondarie.

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

2

Utilizzando l' UIViewestensione Swift :

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

2

In obiettivo-C, vai avanti e crea un metodo di categoria fuori dalla classe UIView.

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

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

2
Sebbene questo codice possa rispondere alla domanda, fornire un contesto aggiuntivo riguardo a come e / o perché risolve il problema migliorerebbe il valore a lungo termine della risposta.
Nic3500,

1

Per rimuovere la sintassi di tutte le visualizzazioni secondarie:

- (void)makeObjectsPerformSelector:(SEL)aSelector;

Utilizzo:

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

Questo metodo è presente nel file NSArray.h e utilizza l'interfaccia NSArray (NSExtendedArray)


1

Se stai usando Swift, è semplice come:

subviews.map { $0.removeFromSuperview }

È simile in filosofia makeObjectsPerformSelectorall'approccio, tuttavia con un po 'più di sicurezza del tipo.


Questo è semanticamente errato, mapnon dovrebbe provocare effetti collaterali. Inoltre, lo stesso risultato può essere raggiunto tramite forEach.
Cristik,

0

Per iOS6 utilizzando il layout automatico ho dovuto aggiungere un po 'di codice per rimuovere anche i vincoli.

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

Sono sicuro che esiste un modo più ordinato per farlo, ma ha funzionato per me. Nel mio caso non ho potuto usare un direct in [tagview removeConstraints:tagview.constraints]quanto c'erano vincoli impostati in XCode che venivano eliminati.


0

In monotouch / xamarin.ios questo ha funzionato per me:

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

-10

Per rimuovere tutte le sottoview dalle superview:

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

Un paio di errori importanti qui @Pravin. Innanzitutto, avresti bisogno di definire 'oggetto' come UIView * altrimenti otterrai un errore del compilatore con [object removeFromSuperview]. Secondo, il tuo ciclo for sta già diminuendo iCount, quindi ne stai saltando uno in più con la tua linea iCount. E infine, ci sono due approcci funzionanti e corretti sopra e il tuo non è né più elegante né più veloce.
amergin

5
ogni iterazione che fai iCount++e iCount--, lasciando l'indice lo stesso, quindi sarà un ciclo infinito se [oSubView count]>0. Questo è sicuramente un codice errato e NON UTILIZZABILE .
Daniel,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.