¿Eliminar todas las subvistas?

316

Cuando mi aplicación vuelve a su controlador de vista raíz, en el viewDidAppear:método necesito eliminar todas las subvistas.

¿Cómo puedo hacer esto?

Ian Vink
fuente

Respuestas:

569

Editar: Con gracias a cocoafan : Esta situación es confusa por el hecho de que NSViewy UIViewcosas maniobran en forma diferente. Para NSView(solo desarrollo de Mac de escritorio), simplemente puede usar lo siguiente:

[someNSView setSubviews:[NSArray array]];

Para UIView(solo desarrollo de iOS), puede usar de forma segura makeObjectsPerformSelector:porque la subviewspropiedad devolverá una copia de la matriz de subvistas:

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

Gracias a Tommy por señalar que makeObjectsPerformSelector:parece modificar la subviewsmatriz mientras se enumera (lo que hace para NSView, pero no para UIView).

Consulte esta pregunta SO para obtener más detalles.

Nota: El uso de cualquiera de estos dos métodos eliminará todas las vistas que contenga su vista principal y las liberará , si no se conservan en otro lugar. De la documentación de Apple sobre removeFromSuperview :

Si la vista general del receptor no es nula, este método libera al receptor. Si planea reutilizar la vista, asegúrese de conservarla antes de llamar a este método y asegúrese de liberarla según corresponda cuando haya terminado con ella o después de agregarla a otra jerarquía de vistas.

e.James
fuente
99
¿Estás seguro de que esto es seguro? Muta la lista mientras la itera, y no puedo encontrar una declaración definitiva en la documentación de Apple.
Tommy
8
@ Tommy: Ese es un buen punto. Google buscó la respuesta: UIViewdevuelve una copia de la subviewsmatriz mutable, por lo que este código simplemente funciona. Historia completamente diferente en el escritorio, donde el mismo código arrojará una excepción. Ver stackoverflow.com/questions/4665179/…
e.James
3
UIView no responde a setSubviews :, ¿verdad?
cocoafan
44
la forma de Xamarin: someUIView.Subviews.All (p => p.RemoveFromSuperview);
Benoit Jadinon
3
@BenoitJadinon - no compilará - parece que quieres decir abusar de All para realizar ForEach, entonces someUIView.Subviews.All( v => { v.RemoveFromSuperview(); return true; } );. En mi humilde opinión más limpio para decir lo que quiere decir: someUIView.Subviews.ToList().ForEach( v => v.RemoveFromSuperview() );.
ToolmakerSteve
173

Obtenga todas las subvistas de su controlador raíz y envíe a cada una una removeFromSuperview:

NSArray *viewsToRemove = [self.view subviews];
for (UIView *v in viewsToRemove) {
    [v removeFromSuperview];
}
Matthew McGoogan
fuente
+1 y gracias. También debería haberlo usado self.viewcomo tú.
e.James
2
¿¡Por qué no!? for (UIView *v in [self.view subviews])es más fácil
Frade
44
@Frade Es mucho más claro y detallado de la forma en que lo hizo. Detallado y legible> ahorro de pulsaciones de teclas
taylorcressy
33
@taylorcressy Debería haber dicho "la legibilidad es más importante que guardar las pulsaciones de teclas" en lugar de "legibilidad> guardar las pulsaciones de teclas" y luego su comentario sería más legible. :-)
arlomedia
1
No olvidemos el hecho de que si [self.view subviews] realiza algún cálculo oculto, colocarlo directamente en el bucle for podría hacer que esos cálculos se realicen una y otra vez. Declararlo antes del ciclo asegura que solo se realicen una vez.
Brian Sachetta
116

En Swift puede usar un enfoque funcional como este:

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

Como comparación, el enfoque imperativo se vería así:

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

Sin embargo, estos fragmentos de código solo funcionan en iOS / tvOS , las cosas son un poco diferentes en macOS.

Jeehut
fuente
44
(subviews as [UIView]).map { $0.removeFromSuperview() }
DeFrenZ
8
no es funcional ya que una función devuelve un valor y esto simplemente descarta el resultado de .map. Este es un efecto secundario puro y se maneja mejor así:view.subviews.forEach() { $0.removeFromSuperview() }
Martin Algesten
1
Tienes razón, Martin, estoy de acuerdo contigo. Simplemente no sabía que había un método forEach () en las matrices. ¿Cuándo se agregó o acabo de supervisarlo? ¡He actualizado mi respuesta!
Jeehut
1
Soy tan vago que incluso si supiera cómo borrar subvistas, todavía vengo aquí para copiar / pegar su fragmento y poner un +1
Vilmir
13

Si desea eliminar todas las subvistas en su UIView (aquí yourView), entonces escriba este código en el botón de clic

[[yourView subviews] makeObjectsPerformSelector: @selector(removeFromSuperview)];
Mohd Rahib
fuente
12
¡Bienvenido a Stack Overflow! ¿Consideraría agregar alguna narrativa para explicar por qué funciona este código y qué lo convierte en una respuesta a la pregunta? Esto sería muy útil para la persona que hace la pregunta y para cualquier otra persona que se presente. Además, la respuesta ya aceptada incluye código que es esencialmente el mismo que este.
Andrew Barber
55
¿Cómo podría esto ayudar más que la respuesta aceptada? Es idéntico. ¿Por qué escribir esto?
Rambatino
8

Esto solo se aplica a OSX ya que en iOS se guarda una copia de la matriz

Al eliminar todas las subvistas, es una buena idea comenzar a eliminar al final de la matriz y seguir eliminando hasta llegar al principio. Esto se puede lograr con estas dos líneas de código:

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 (menos eficiente, pero más legible)

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

NOTA

NO deberías eliminar las subvistas en orden normal, ya que puede causar un bloqueo si se elimina una instancia de UIView antes de que el removeFromSuperviewmensaje se haya enviado a todos los objetos de la matriz. (Obviamente, eliminar el último elemento no causaría un bloqueo)

Por lo tanto, el código

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

debería NO ser usado.

Cita de la documentación de Apple sobre makeObjectsPerformSelector :

Envía a cada objeto en la matriz el mensaje identificado por un selector dado, comenzando con el primer objeto y continuando a través de la matriz hasta el último objeto.

(que sería la dirección incorrecta para este propósito)

Daniel
fuente
¿Puedes dar un ejemplo de a qué te refieres? No sé a qué se refiere como "elemento" ¿Y cómo se eliminarían estos elementos antes de llamar a removeFromSuperView?
el reverendo
Pero, ¿cómo se puede eliminar una instancia de UIView al llamar a este método? ¿Te refieres a eliminado de la matriz de subvista?
El reverendo
Cuando removeFromSuperviewfinalice, la UIView se eliminará de la matriz, y si no hay otras instancias vivas con una relación fuerte con la UIView, la UIView también se eliminará. Esto puede causar una excepción fuera de límite.
Daniel
1
Gotcha! Gracias. Creo que está obteniendo una copia de la matriz de subvistas en IOS. En cualquier caso, sería una buena idea hacer una copia usted mismo si desea eliminar las subvistas.
El reverendo
@simpleBob: ¿leyó los comentarios escritos en 2011 sobre la respuesta aceptada? Según esos comentarios, en iOS, [yourView subviews]devuelve una COPIA de la matriz, por lo tanto, es seguro. (TEN EN CUENTA que en OSX, lo que dices es correcto.)
ToolmakerSteve
4

Prueba de esta manera swift 2.0

view.subviews.forEach { $0.removeFromSuperview() }
William Hu
fuente
2
¿No ves la fecha fecha de respuesta que soy anterior? ¿Por qué no pegar mi enlace de respuesta en esa respuesta?
William Hu
1
Correcto ... la respuesta se publicó antes que la suya, sin embargo, la forEachsolución basada se agregó después de la suya, me perdí eso. Disculpas
Cristik
4

Use el siguiente código para eliminar todas las subvistas.

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

Usando la UIViewextensión Swift :

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

En el objetivo C, siga adelante y cree un método de categoría fuera de la clase UIView.

- (void)removeAllSubviews
{
    for (UIView *subview in self.subviews)
        [subview removeFromSuperview];
}
Rickster
fuente
2
view.subviews.forEach { $0.removeFromSuperview() }
MAGiGO
fuente
2
Si bien este código puede responder la pregunta, proporcionar un contexto adicional con respecto a cómo y / o por qué resuelve el problema mejoraría el valor a largo plazo de la respuesta.
Nic3500
1

Para eliminar todas las subvistas Sintaxis:

- (void)makeObjectsPerformSelector:(SEL)aSelector;

Uso:

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

Este método está presente en el archivo NSArray.h y utiliza la interfaz NSArray (NSExtendedArray)

Jayprakash Dubey
fuente
1

Si usa Swift, es tan simple como:

subviews.map { $0.removeFromSuperview }

Es similar en filosofía al makeObjectsPerformSelectorenfoque, sin embargo, con un poco más de seguridad de tipo.

lms
fuente
Esto es semánticamente incorrecto, mapno debería provocar efectos secundarios. Además, se puede lograr el mismo resultado a través de forEach.
Cristik
0

Para ios6 usando autolayout, también tuve que agregar un poco de código para eliminar las restricciones.

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

Estoy seguro de que hay una forma más ordenada de hacer esto, pero funcionó para mí. En mi caso, no pude usar un directo [tagview removeConstraints:tagview.constraints]ya que había restricciones establecidas en XCode que se estaban borrando.

Michael Anderson
fuente
0

En monotouch / xamarin.ios esto funcionó para mí:

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

Para eliminar todas las subvistas de las supervistas:

NSArray *oSubView = [self subviews];
for(int iCount = 0; iCount < [oSubView count]; iCount++)
{
    id object = [oSubView objectAtIndex:iCount];
    [object removeFromSuperview];
    iCount--;
}
Pravin
fuente
Un par de errores importantes aquí @Pravin. Primero, necesitaría que 'objeto' se definiera como una UIView * de lo contrario obtendría un error de compilación con [object removeFromSuperview]. En segundo lugar, su bucle for ya está disminuyendo iCount, por lo que omite uno adicional con su línea iCount--. Y finalmente, hay dos enfoques correctos y de trabajo arriba y el tuyo no es más elegante ni más rápido.
amergin
55
cada iteración que haces iCount++y iCount--, dejando el índice igual, por lo que será un bucle infinito si [oSubView count]>0. Este es definitivamente un código defectuoso y NO UTILIZABLE .
Daniel