如何删除所有子视图?

331

当我的应用程序返回到其根视图控制器时,在viewDidAppear:方法中我需要删除所有的子视图。

我该如何做?

15个回答

573

编辑:感谢cocoafan的帮助:由于NSViewUIView处理方式不同,这种情况变得混乱。对于NSView(仅适用于桌面Mac开发),您可以简单地使用以下方法:

[someNSView setSubviews:[NSArray array]];
在iOS开发中,对于UIView类型的对象,可以安全地使用makeObjectsPerformSelector:方法,因为subviews属性会返回子视图数组的一个副本
[[someUIView subviews]
 makeObjectsPerformSelector:@selector(removeFromSuperview)];

感谢Tommy指出makeObjectsPerformSelector:在枚举期间修改了subviews数组(这对于NSView是正确的,但对于UIView不是)。

请参见此SO问题以获取更多详细信息。

注意:使用这两种方法之一将删除您的主视图包含的每个视图并进行释放(如果它们没有在其他地方保留)。从苹果关于removeFromSuperview的文档中可以看到:

如果接收者的父视图不为nil,则此方法会释放接收者。如果计划重用视图,请在调用此方法之前保留它,并在处理完毕后或将其添加到另一个视图层次结构之后适当释放它。


9
你确定这是安全的吗?在迭代列表时它对其进行了改变,我无法在苹果的文档中找到明确的说明。 - Tommy
8
@Tommy:那是个好观点。通过一些谷歌搜索,找到了答案:UIView 返回 subviews 可变数组的 副本,所以这段代码可以正常工作。然而在桌面端情况却截然不同,同样的代码会抛出异常。参见 https://dev59.com/6FPTa4cB1Zd3GeqPiFCZ - e.James
3
UIView没有响应setSubviews:方法,对吗? - cocoafan
4
Xamarin的方法:someUIView.Subviews.All(p => p.RemoveFromSuperview);翻译:Xamarin 的方法是在一个视图中移除所有子视图,代码为 someUIView.Subviews.All(p => p.RemoveFromSuperview); - Benoit Jadinon
3
@BenoitJadinon - 无法编译 - 您似乎想利用All执行ForEach的操作,因此请使用someUIView.Subviews.All( v => { v.RemoveFromSuperview(); return true; } );。个人认为更清晰明了的做法是:someUIView.Subviews.ToList().ForEach( v => v.RemoveFromSuperview() ); - ToolmakerSteve
显示剩余4条评论

174

从根控制器获取所有子视图,并将每个视图发送一个removeFromSuperview:

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

+1,谢谢。我也应该像你一样使用self.view - e.James
2
为什么不呢?!for (UIView *v in [self.view subviews]) 这样更简单。 - Frade
4
@Frade 他用的方式更加清晰和详尽。详尽和易读 > 节省按键 - taylorcressy
34
@taylorcressy,你应该说“易读性比省略击键更重要”,而不是“易读性>省略击键”,这样你的评论会更易读。 :-) - arlomedia
1
不要忘记这个事实,即如果[self.view subviews]在幕后执行任何计算,直接将其放入for循环中可能会导致这些计算一遍又一遍地执行。在循环之前声明它可以确保它们只执行一次。 - Brian Sachetta

143

Swift 中,您可以像这样使用 函数式方法(functional approach)

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

作为对比,命令式方法看起来是这样的:

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

这些代码片段只能在 iOS / tvOS 上工作,但在 macOS 上有些许不同。


4
这段代码的意思是:从subviews这个UIView数组中遍历出每一个子视图,然后将其从父视图中移除。 - DeFrenZ
11
这段代码并不是一个函数,因为函数需要返回一个值,而这里只是丢弃了 .map 的结果。这是一个纯副作用,最好像这样处理:view.subviews.forEach() { $0.removeFromSuperview() } - Martin Algesten
1
你是对的,马丁,我同意你的观点。我只是不知道数组上有一个forEach()方法。它是什么时候添加的?还是我只是忽略了它?我已经更新了我的答案! - Jeehut
1
我太懒了,即使我知道如何清除子视图,我仍然来这里复制/粘贴你的片段并加上+1。 - Vilmir

14

如果您想删除UIView(这里是yourView)上的所有子视图,则在按钮单击时编写以下代码:

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

12
欢迎来到Stack Overflow!您能否考虑添加一些说明,解释为什么这段代码有效,并回答问题的原因?这将对提问者和任何其他人非常有帮助。此外,已经被接受的答案包括与此基本相同的代码。 - Andrew Barber
5
这句话的意思是什么:相较于已被接受的答案,这个能更有所帮助吗?它们完全一样。为什么要写这个呢? - Rambatino

9
这仅适用于OSX,因为在iOS中会保留数组的副本。
当删除所有子视图时,最好从数组末尾开始删除,并一直删除到达开头。可以使用以下两行代码实现:
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();
}

或者(效率较低,但更易读)
for subview in mySuperView.subviews.reverse() {
    subview.removeFromSuperview()
}

注意

正常情况下,您不应该按顺序删除子视图,因为在向所有数组对象发送removeFromSuperview消息之前删除UIView实例可能会导致崩溃。(显然,删除最后一个元素不会导致崩溃)

因此,代码应该是:

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

不应使用。

Apple关于makeObjectsPerformSelector的文档引用:

向数组中的每个对象发送由给定选择器标识的消息,从第一个对象开始,通过数组继续到最后一个对象。

(这对此目的来说是错误的方向)


请你能否举个例子来说明你所指的是什么?我不知道你所说的“元素”是什么。在调用removeFromSuperView之前,这些元素应该如何被移除? - the Reverend
但是在调用这个方法的时候,如何删除UIView的实例?你是指从子视图数组中移除吗? - the Reverend
removeFromSuperview 完成后,UIView 将从数组中移除,如果没有其他具有强关系的实例与 UIView 存在,则 UIView 也将被删除。这可能会导致越界异常。 - Daniel
1
明白了!谢谢。我认为你正在获取 IOS 上的子视图数组的副本。无论如何,如果您想要删除子视图,最好自己制作一个副本。 - the Reverend
@simpleBob - 你有没有看到2011年对被接受答案的评论?根据那些评论,在iOS上,[yourView subviews]返回一个数组的COPY,因此是安全的。(请注意,在OSX上,你所说的是正确的。) - ToolmakerSteve
无法在不可变值上使用可变成员:'subviews'是只读属性。 - JBarros35

6

尝试这种方式 swift 2.0

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

2
你没看到我的回答日期比那个早吗?为什么不把我的回答链接粘贴到那个回答里呢? - William Hu
1
没错...在你之前已经发布了答案,但是基于forEach的解决方案是在你之后添加的,我没有注意到。抱歉。 - Cristik

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

2
虽然这段代码可能回答了问题,但提供有关它如何以及/或为什么解决问题的附加上下文将改善答案的长期价值。 - Nic3500
非常好的答案。 - Naresh

4
在Objective-C中,可以在UIView类中创建一个分类方法。
- (void)removeAllSubviews
{
    for (UIView *subview in self.subviews)
        [subview removeFromSuperview];
}

4
使用Swift的UIView扩展:
extension UIView {
    func removeAllSubviews() {
        for subview in subviews {
            subview.removeFromSuperview()
        }
    }
}

4
使用以下代码来删除所有子视图。
for (UIView *view in [self.view subviews]) 
{
 [view removeFromSuperview];
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接