Remover todas as subvisões?


316

Quando meu aplicativo retorna ao seu controlador de visualização raiz, no diretório viewDidAppear: método preciso remover todas as subvisões.

Como posso fazer isso?

Respostas:


569

Edit: Com agradecimentos a cocoafan : Esta situação é confusa pelo fato de que NSViewe UIViewlidar com as coisas de maneira diferente. Para NSView(apenas desenvolvimento para desktops Mac), você pode simplesmente usar o seguinte:

[someNSView setSubviews:[NSArray array]];

Para UIView(apenas desenvolvimento iOS), você pode usar com segurança makeObjectsPerformSelector:porque a subviewspropriedade retornará uma cópia da matriz de sub-visualizações:

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

Obrigado a Tommy por apontar que makeObjectsPerformSelector:parece modificar a subviewsmatriz enquanto ela está sendo enumerada (o que serve para NSView, mas não para UIView).

Por favor, veja esta pergunta para mais detalhes.

Nota: O uso de um desses dois métodos removerá todas as visualizações que a visualização principal contém e as liberará , se não forem mantidas em outro lugar. Na documentação da Apple sobre removeFromSuperview :

Se a super visão do receptor não for nula, esse método libera o receptor. Se você planeja reutilizar a visualização, mantenha-a antes de chamar este método e libere-a conforme apropriado quando terminar ou após adicioná-la a outra hierarquia de visualização.


9
Tem certeza de que isso é seguro? Ele modifica a lista enquanto a itera, e não consigo encontrar uma declaração definitiva na documentação da Apple.
21411 Tommy

8
@ Tommy: Esse é um bom ponto. Alguns pesquisadores pesquisaram a resposta: UIViewretorna uma cópia da subviewsmatriz mutável, portanto esse código simplesmente funciona. História completamente diferente na área de trabalho, onde o mesmo código gera uma exceção. Veja stackoverflow.com/questions/4665179/…
e.James

3
O UIView não responde ao setSubviews :, responde?
cocoafan

4
da maneira Xamarin: someUIView.Subviews.All (p => p.RemoveFromSuperview);
Benoit Jadinon

3
@BenoitJadinon - não será compilado - você parece querer abusar do All para executar o ForEach, por isso someUIView.Subviews.All( v => { v.RemoveFromSuperview(); return true; } );. IMHO mais limpo para dizer o que quer dizer: someUIView.Subviews.ToList().ForEach( v => v.RemoveFromSuperview() );.
Home

173

Obtenha todas as subvisões do seu controlador raiz e envie a removeFromSuperview:

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

+1 e obrigado. Eu também deveria ter usado self.viewcomo você.
precisa saber é o seguinte

2
Por que não!? for (UIView *v in [self.view subviews])é mais fácil
Frade

4
@Frade É muito mais claro e detalhado do jeito que ele fez. Detalhado e legibilidade> salvar as teclas digitadas
taylorcressy

33
@taylorcressy Você deveria ter dito "a legibilidade é mais importante do que salvar pressionamentos de tecla" em vez de "legibilidade> salvar pressionamentos de tecla" e, em seguida, seu comentário seria mais legível. :-)
arlomedia 14/01

1
Não devemos esquecer o fato de que, se [subvisões de self.view] executar algum cálculo sob o capô, colocá-lo diretamente no loop for poderá fazer com que esses cálculos sejam executados repetidamente. Declará-lo antes do loop garante que eles sejam executados apenas uma vez.
18718 Brian Sachetta

116

No Swift, você pode usar uma abordagem funcional como esta:

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

Como comparação, a abordagem imperativa ficaria assim:

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

Esses trechos de código funcionam apenas no iOS / tvOS , porém, as coisas são um pouco diferentes no macOS.


4
(subviews as [UIView]).map { $0.removeFromSuperview() }
precisa saber é

8
não é funcional, pois uma função retorna um valor e isso apenas descarta o resultado do .map. este é um efeito colateral pura e é melhor tratadas assim:view.subviews.forEach() { $0.removeFromSuperview() }
Martin Algesten

1
Você está certo, Martin, eu concordo com você. Eu simplesmente não sabia que havia um método forEach () em matrizes. Quando foi adicionado ou eu apenas o supervisionei? Eu atualizei minha resposta!
Jeehut 25/11

1
Eu sou tão preguiçoso que, mesmo se eu soubesse como subviews claras, eu ainda vim aqui para copiar / colar o trecho e colocar um +1
Vilmir

13

Se você deseja remover todas as subvisões no seu UIView (aqui yourView), escreva esse código no seu botão, clique em:

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

12
Bem-vindo ao Stack Overflow! Você consideraria adicionar alguma narrativa para explicar por que esse código funciona e o que o torna uma resposta para a pergunta? Isso seria muito útil para a pessoa que fez a pergunta e para qualquer outra pessoa que aparecer. Além disso, a resposta já aceita inclui código que é essencialmente o mesmo que esse.
Andrew Barber

5
Como isso poderia ajudar mais do que a resposta aceita: é idêntico. Por que escrever isso?
Rambatino 29/05

8

Isso se aplica apenas ao OSX, já que no iOS uma cópia da matriz é mantida

Ao remover todas as subvisões, é uma boa idéia começar a excluir no final da matriz e continuar excluindo até chegar ao início. Isso pode ser realizado com essas duas linhas 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();
}

ou (menos eficiente, mas mais legível)

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

NOTA

Você NÃO deve remover as subvisões na ordem normal, pois isso pode causar uma falha se uma instância do UIView for excluída antes que a removeFromSuperviewmensagem seja enviada a todos os objetos da matriz. (Obviamente, excluir o último elemento não causaria uma falha)

Portanto, o código

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

NÃO deve ser usado.

Citações da documentação da Apple sobre makeObjectsPerformSelector :

Envia para cada objeto na matriz a mensagem identificada por um determinado seletor, começando com o primeiro objeto e continuando através da matriz até o último objeto.

(qual seria a direção errada para esse fim)


Você pode fazer um exemplo do que você está se referindo? Não sei a que você está se referindo como "elemento" E como esses elementos seriam removidos antes de chamar removeFromSuperView?
o Reverendo

Mas como uma instância do UIView pode ser excluída ao chamar esse método? Você quer dizer removido da matriz subview?
o reverendo

Quando removeFromSuperviewterminar, o UIView será removido da matriz e, se não houver outras instâncias vivas com uma forte relação com o UIView, o UIView também será excluído. Isso pode causar uma exceção fora do limite.
Daniel

1
Peguei vocês! Obrigado. Eu acho que você está recebendo uma cópia da matriz de subviews no IOS. De qualquer forma, seria uma boa ideia fazer uma cópia se quiser remover subvisões.
o reverendo

@simpleBob - você leu os comentários escritos em 2011 sobre a resposta aceita? De acordo com esses comentários, no iOS, [yourView subviews]retorna uma cópia do array, portanto, é seguro. (Note que no OSX, o que você diz é correto.)
ToolmakerSteve

4

Experimente desta maneira o Swift 2.0

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

2
Você não vê a data em que eu respondo antes? Por que não colar meu link de resposta nessa resposta?
William Hu

1
Certo ... a resposta foi postada antes da sua, no entanto, a forEachsolução baseada foi adicionada após a sua, eu perdi isso. Desculpas.
Cristik 27/01

4

Use o código a seguir para remover todas as subvisões.

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

2

Usando a UIViewextensão Swift :

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

2

No objetivo C, vá em frente e crie um método de categoria fora da classe UIView.

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

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

2
Embora esse código possa responder à pergunta, fornecer um contexto adicional sobre como e / ou por que resolve o problema melhoraria o valor a longo prazo da resposta.
Nic3500

1

Para remover todas as subviews Sintaxe:

- (void)makeObjectsPerformSelector:(SEL)aSelector;

Uso:

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

Este método está presente no arquivo NSArray.h e usa a interface NSArray (NSExtendedArray)


1

Se você estiver usando o Swift, é tão simples quanto:

subviews.map { $0.removeFromSuperview }

É semelhante em filosofia à makeObjectsPerformSelectorabordagem, porém com um pouco mais de segurança de tipo.


Isso é semanticamente incorreto, mapnão deve resultar em efeitos colaterais. Além disso, o mesmo resultado pode ser alcançado via forEach.
Cristik 26/10/19

0

Para o ios6 usando o autolayout, tive que adicionar um pouco de código para remover as restrições também.

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

Tenho certeza de que há uma maneira mais clara de fazer isso, mas funcionou para mim. No meu caso, eu não poderia usar um direto, [tagview removeConstraints:tagview.constraints]pois havia restrições definidas no XCode que estavam sendo eliminadas.


0

No monotouch / xamarin.ios, isso funcionou para mim:

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

-10

Para remover todas as subvisões das superviews:

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

Alguns dos principais erros aqui @Pravin. Primeiro, você precisa que 'object' seja definido como um UIView *, caso contrário, você receberá um erro do compilador com [object removeFromSuperview]. Segundo, seu loop for já está diminuindo o iCount, de modo que você está pulando um extra com sua linha iCount--. E, finalmente, há duas abordagens corretas e corretas acima e a sua não é mais elegante nem mais rápida.
Amergin

5
cada iteração que você faz iCount++e iCount--, deixando o índice o mesmo, então haverá um loop infinito se [oSubView count]>0. Este é definitivamente um código de buggy e NÃO UTILIZÁVEL .
Daniel
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.