【问题标题】:How to remove all subviews?如何删除所有子视图?
【发布时间】:2011-01-10 11:48:04
【问题描述】:

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

我该怎么做?

【问题讨论】:

    标签: ios subview


    【解决方案1】:

    这仅适用于 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 documentation about makeObjectsPerformSelector:

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

    (这将是错误的方向)

    【讨论】:

    • 你能举个例子来说明你指的是什么吗?不知道您指的是什么“元素”以及如何在调用 removeFromSuperView 之前删除这些元素?
    • 但是如何在调用这个方法时删除 UIView 的实例呢?你的意思是从子视图数组中删除?
    • removeFromSuperview完成时,UIView将从数组中移除,如果没有其他与UIView有强关联的活实例,UIView也会被删除。这可能会导致超出范围的异常。
    • 明白了!谢谢你。我认为您在 IOS 上获得了 subviews 数组的副本。无论如何,如果您想删除子视图,最好自己制作一份副本。
    • @simpleBob - 您是否阅读了 2011 年编写的关于已接受答案的 cmets?根据那些 cmets,在 iOS 上,[yourView subviews] 返回数组的副本,因此是安全的。 (请注意,在 OSX 上,您所说的是正确的。)
    【解决方案2】:
    view.subviews.forEach { $0.removeFromSuperview() }
    

    【讨论】:

    • 虽然此代码可能会回答问题,但提供有关它如何和/或为什么解决问题的额外上下文将提高​​答案的长期价值。
    • 非常好的答案。
    【解决方案3】:

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

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

    作为比较,命令式方法如下所示:

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

    这些代码 sn-ps 仅适用于 iOS / tvOS,但在 macOS 上有些不同。

    【讨论】:

    • (subviews as [UIView]).map { $0.removeFromSuperview() }
    • 它不起作用,因为函数返回一个值,而这只是丢弃了.map 的结果。这是一个纯粹的副作用,最好这样处理:view.subviews.forEach() { $0.removeFromSuperview() }
    • 你说得对,马丁,我同意你的看法。我只是不知道数组上有一个 forEach() 方法。它是什么时候添加的,还是我只是监督它?我已经更新了我的答案!
    • 我太懒了,即使我知道如何清除子视图,我还是来这里复制/粘贴你的sn-p并放一个+1
    【解决方案4】:

    使用以下代码删除所有子视图。

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

    【讨论】:

      【解决方案5】:

      在 Objective-C 中,继续从 UIView 类创建一个类别方法。

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

      【讨论】:

        【解决方案6】:

        在 monotouch / xamarin.ios 这对我有用:

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

        【讨论】:

          【解决方案7】:

          如果您想删除 UIView 上的所有子视图(此处为 yourView),请在单击按钮时编写此代码:

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

          【讨论】:

          • 欢迎来到 Stack Overflow!您是否会考虑添加一些叙述来解释为什么此代码有效,以及是什么使它成为问题的答案?这对提出问题的人以及其他任何出现的人都非常有帮助。此外,已经接受的答案包括与此基本相同的代码。
          • 这怎么能比公认的答案更有帮助:它是相同的。为什么要写这个?
          【解决方案8】:

          试试这个方法 swift 2.0

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

          【讨论】:

          • 你没看到我的回答日期早吗?为什么不将我的答案链接粘贴到该答案中?
          • 对...答案发布在您之前,但是基于forEach 的解决方案是在您之后添加的,我错过了。道歉。
          【解决方案9】:

          使用 Swift UIView 扩展:

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

          【讨论】:

            【解决方案10】:

            如果你使用的是 Swift,那么简单如下:

            subviews.map { $0.removeFromSuperview }
            

            它在理念上类似于 makeObjectsPerformSelector 方法,但类型安全性更高。

            【讨论】:

            • 这在语义上是不正确的,map 应该不会产生副作用。另外,通过forEach 可以达到相同的结果。
            【解决方案11】:

            为了从超级视图中删除所有子视图:

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

            【讨论】:

            • 这里有几个主要错误@Pravin。首先,您需要将“object”定义为 UIView*,否则您会收到 [object removeFromSuperview] 的编译器错误。其次,您的 for 循环已经在减少 iCount,因此您正在使用 iCount-- 行跳过一个额外的循环。最后,上面有两种可行且正确的方法,而您的方法既不优雅也不快。
            • 每次迭代iCount++iCount--,保持索引不变,所以如果[oSubView count]&gt;0 将是一个无限循环。这绝对是错误且不可用的代码。
            【解决方案12】:

            为了删除所有子视图语法:

            - (void)makeObjectsPerformSelector:(SEL)aSelector;
            

            用法:

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

            此方法存在于 NSArray.h 文件中,并使用 NSArray(NSExtendedArray) 接口

            【讨论】:

              【解决方案13】:

              对于使用自动布局的 ios6,我还必须添加一些代码来移除约束。

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

              我确信有一种更简洁的方法可以做到这一点,但它对我有用。在我的情况下,我不能直接使用 [tagview removeConstraints:tagview.constraints],因为 XCode 中设置的约束正在被清除。

              【讨论】:

                【解决方案14】:

                编辑:感谢cocoafanNSViewUIView 处理事情的方式不同,这使情况变得混乱。对于NSView(仅限桌面 Mac 开发),您可以简单地使用以下内容:

                [someNSView setSubviews:[NSArray array]];
                

                对于UIView(仅限iOS 开发),您可以安全地使用makeObjectsPerformSelector:,因为subviews 属性将返回子视图数组的副本

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

                感谢Tommy 指出makeObjectsPerformSelector: 似乎在枚举subviews 数组时修改了它(它对NSView 起作用,但对UIView 不起作用)。

                详情请见this SO question

                注意:使用这两种方法中的任何一种都会删除主视图包含的每个视图并释放它们,如果它们没有在其他地方保留。来自 Apple 在removeFromSuperview 上的文档:

                如果receiver的superview不是nil,这个方法释放receiver。如果您打算重用视图,请务必在调用此方法之前保留它,并确保在使用完它或将其添加到另一个视图层次结构后适当地释放它。

                【讨论】:

                • 你确定这是安全的吗?它在迭代列表时会对其进行变异,我无法在 Apple 的文档中找到明确的声明。
                • @Tommy:这是一个很好的观点。一些谷歌搜索找到了答案:UIView 返回subviews 可变数组的副本,所以这段代码可以正常工作。桌面上完全不同的故事,相同的代码将引发异常。见stackoverflow.com/questions/4665179/…
                • UIView 没有响应 setSubviews:,是吗?
                • Xamarin 方式:someUIView.Subviews.All(p => p.RemoveFromSuperview);
                • @BenoitJadinon - 不会编译 - 你的意思似乎是滥用 All 来执行 ForEach,所以someUIView.Subviews.All( v =&gt; { v.RemoveFromSuperview(); return true; } );。恕我直言,清洁工说出你的意思:someUIView.Subviews.ToList().ForEach( v =&gt; v.RemoveFromSuperview() );.
                【解决方案15】:

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

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

                【讨论】:

                • +1,谢谢。我也应该像你一样使用self.view
                • 为什么不呢!? for (UIView *v in [self.view subviews]) 更简单
                • @Frade 他的做法更清晰、更详细。详细和可读性 > 节省击键
                • @taylorcressy 您应该说“可读性比保存击键更重要”而不是“可读性> 保存击键”,然后您的评论将更具可读性。 :-)
                • 我们不要忘记这样一个事实,如果 [self.view subviews] 在后台执行任何计算,将其直接放在 for 循环中可能会导致这些计算被一遍又一遍地执行。在循环之前声明它可以确保它们只执行一次。
                猜你喜欢
                • 1970-01-01
                • 2012-03-20
                • 2014-08-10
                • 1970-01-01
                • 1970-01-01
                • 2021-02-06
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多