How to remove all subviews of a view in Swift?
Asked Answered
S

22

224

I'm looking for a simple method to remove at once all subviews from a superview instead of removing them one by one.

//I'm trying something like this, but is not working
let theSubviews : Array = container_view.subviews
for (view : NSView) in theSubviews {
    view.removeFromSuperview(container_view)
}

What I am missing?

UPDATE

My app has a main container_view. I have to add different other views as subviews to container_view in order to provide a sort of navigation.

So, when clicking the button to "open" a particular page, I need to remove allsubviews and add the new one.

UPDATE 2 - A working solution (OS X)

I guess Apple fixed it.

Now it is more easy than ever, just call:

for view in containerView.subviews{
    view.removeFromSuperview()
}
Sueannsuede answered 19/6, 2014 at 17:26 Comment(3)
I'd like to point out that @sulthan's answer, while buried with the rags, is the superior answer: #24313260Ultramundane
@ChristopherSwasey Swift 4 gives an error: Cannot assign to property: 'subviews' is a get-only property. :(Bonhomie
@WilliamT.Mallard how many times does it have to be repeated that this method and question is about MacOS and not iOS?Mouldon
S
413

EDIT: (thanks Jeremiah / Rollo)

By far the best way to do this in Swift for iOS is:

view.subviews.forEach({ $0.removeFromSuperview() }) // this gets things done
view.subviews.map({ $0.removeFromSuperview() }) // this returns modified array

^^ These features are fun!

let funTimes = ["Awesome","Crazy","WTF"]
extension String { 
    func readIt() {
        print(self)
    }
}

funTimes.forEach({ $0.readIt() })

//// END EDIT

Just do this:

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

Or if you are looking for a specific class

for view:CustomViewClass! in self.view.subviews {
        if view.isKindOfClass(CustomViewClass) {
            view.doClassThing()
        }
    }
Skyeskyhigh answered 10/7, 2014 at 1:13 Comment(9)
In Xcode 7.0 beta 6 this generates a warning: "result of call to 'map' is unused". I hope this gets fixed in the final version.Halter
I noticed that as well! I'll update the post once Xcode comes out of beta and the problem still persists.Skyeskyhigh
The warning, result of call to 'map' is unused , is not an error. Array.map in most languages will return the modified array. The equivalent method which doesn't return an array, would be view.subviews.forEach.Lakitalaks
for view:CustomViewClass! in self.view.subviews where view.isKindOfClass(CustomViewClass) { view.doClassThing() }Custody
I would argue that this is"By far the best way to do this in Swift" , see https://mcmap.net/q/118038/-how-to-remove-all-subviews-of-a-view-in-swiftFruity
@Fruity view.subviews is now a read-only property in Swift so the suggestion wouldn't work now.Skyeskyhigh
The documentation kinda says another thing.Fruity
@Skyeskyhigh I think you're confusing NSView and UIView, for UIView indeed, the subviews property is readonly.Fruity
@Fruity I stand corrected! Thanks for pointing that out. Indeed I was confusing the two. I'll update my answer.Skyeskyhigh
S
46

For iOS/Swift, to get rid of all subviews I use:

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

to get rid of all subviews of a particular class (like UILabel) I use:

for v in view.subviews{
   if v is UILabel{
      v.removeFromSuperview()
   }
}
Screeching answered 14/2, 2015 at 13:43 Comment(0)
J
40

The code can be written simpler as following.

view.subviews.forEach { $0.removeFromSuperview() }
Janus answered 24/11, 2015 at 7:14 Comment(0)
G
24

This should be the simplest solution.

let container_view: NSView = ...
container_view.subviews = []

(see Remove all subviews? for other methods)


Note this is a MacOS question and this answer works only for MacOS. It does not work on iOS.

Gypsum answered 19/6, 2014 at 18:40 Comment(7)
This is definitely the simplest way... removeFromSuperview is automatically called for each view that is no longer in the subviews array.Ultramundane
@datayeah They are not but there is a big difference between NSView (OS X) and UIView (iOS).Gypsum
@Gypsum you're right. I came here for the Swift answer for UIView and did'nt read your whole code snippet ;)Woken
I'm more inclined to create an extension to implement removeAllSubviews() on UIView. Easier to remember.Mastoiditis
Swift 4 gives an error: Cannot assign to property: 'subviews' is a get-only property. :(Bonhomie
@WilliamT.Mallard This was originally an OS X question and this is OS X answer.Gypsum
@AmrAngry Again, I repeat, this was always a Mac OS question, for NSView, not iOS and UIView. Only people don't care to read the question and tags therefore it got filled with iOS answers.Gypsum
A
24

Swift 5

I create 2 different methods to remove subview. And it's much easier to use if we put them in extension

extension UIView {
    /// Remove all subview
    func removeAllSubviews() {
        subviews.forEach { $0.removeFromSuperview() }
    }

    /// Remove all subview with specific type
    func removeAllSubviews<T: UIView>(type: T.Type) {
        subviews
            .filter { $0.isMember(of: type) }
            .forEach { $0.removeFromSuperview() }
    }
}
Archi answered 8/8, 2019 at 15:5 Comment(0)
H
14

Try this:

for view in container_view.subviews {
    view.removeFromSuperview()
}
Hypermeter answered 19/6, 2014 at 18:6 Comment(0)
S
11

I don't know if you managed to resolve this but I have recently experienced a similar problem where the For loop left one view each time. I found this was because the self.subviews was mutated (maybe) when the removeFromSuperview() was called.

To fix this I did:

let subViews: Array = self.subviews.copy()
for (var subview: NSView!) in subViews
{
    subview.removeFromSuperview()
}

By doing .copy(), I could perform the removal of each subview while mutating the self.subviews array. This is because the copied array (subViews) contains all of the references to the objects and is not mutated.

EDIT: In your case I think you would use:

let theSubviews: Array = container_view.subviews.copy()
for (var view: NSView!) in theSubviews
{
    view.removeFromSuperview()
}
Spinozism answered 31/7, 2014 at 19:50 Comment(1)
Hello, try the following although there is probably a much more elegant way to do this: let subViewsArray: Array = (parentView.subviews as NSArray).copy() as Array<NSView>Spinozism
O
11

Extension for remove all subviews, it is quickly removed.

import Foundation
import UIKit

extension UIView {
    /// Remove allSubView in view
    func removeAllSubViews() {
        self.subviews.forEach({ $0.removeFromSuperview() })
    }

}
Orchestral answered 7/7, 2015 at 21:1 Comment(2)
why map? foreach maybe ? subviews.forEach { $0.removeFromSuperview() }Ultramicroscope
Yes you right @Aleksandr i think when i was writing this, i did not have take my coffeeOrchestral
T
8

Try this:

var subViews = parentView.subviews as Array<UIView>

      for someView in subViews
      {
          someView.removeFromSuperview()
      }

UPDATE: If you are feeling adventurous then you can make an extension on the UIView as shown below:

extension UIView
{
    func removeAllSubViews()
    {
       for subView :AnyObject in self.subviews
       {
            subView.removeFromSuperview()
       }
    }

}

And call it like this:

parentView.removeAllSubViews()
Terefah answered 19/6, 2014 at 18:33 Comment(5)
2 little things: I'm working on osx, therefore it would be <NSView> right ? Then, I am getting "fatal error: Array index out of range" :/Sueannsuede
Yes, I think on OSX it is NSView. Where do you get the fatal error?Terefah
Are you sure that your parent view has child elements?Terefah
Yep ! If i make a println(parentView.subviews) i get 1Sueannsuede
That is strange! Usually out of range means that you are accessing an index which does not exist and usually I believe it is caused by using a for loop with indexes like i++.Terefah
S
7

In xcodebeta6 this worked out.

    var subViews = self.parentView.subviews
    for subview in subViews as [UIView]   {
        subview.removeFromSuperview()
    }
Successive answered 27/8, 2014 at 12:16 Comment(2)
That is iOS. I need a solution for OS XSueannsuede
Great Solution for IOS +1Camelback
P
5

Swift 3

If you add a tag to your view you can remove a specific view.

for v in (view?.subviews)!
{
    if v.tag == 321
    {
         v.removeFromSuperview()
    }
 }
Phiphenomenon answered 7/2, 2017 at 11:23 Comment(1)
why not view.subviews.filter({$0.tag != 321}).forEach({$0.removeFromSuperview()})?Skald
T
4

I wrote this extension:

extension UIView {
    func lf_removeAllSubviews() {
        for view in self.subviews {
            view.removeFromSuperview()
        }
    }
}

So that you can use self.view.lf_removeAllSubviews in a UIViewController. I'll put this in the swift version of my https://github.com/superarts/LFramework later, when I have more experience in swift (1 day exp so far, and yes, for API I gave up underscore).

Tensile answered 15/9, 2014 at 7:37 Comment(0)
C
3

Your syntax is slightly off. Make sure you cast explicitly.

 let theSubviews : Array<NSView> = container_view.subviews as Array<NSView>
 for view in theSubviews {
     view.removeFromSuperview()
 }
Complication answered 19/6, 2014 at 18:10 Comment(1)
@David Yes it will, I tried to keep it as close to FoxNos's initial code as possible.Complication
A
3

you have to try this

func clearAllScrollSubView ()
{
    let theSubviews = itemsScrollView.subviews

    for (var view) in theSubviews
    {

        if view is UIView
        {
            view.removeFromSuperview()
        }

    }
}
Abradant answered 25/11, 2014 at 0:12 Comment(1)
That is UI not NS. Here we're talking about OS X. If that works even with it by replacing UI with NS, my bad, you were right :)Sueannsuede
L
2

Try this out , I tested this :

  let theSubviews = container_view.subviews
  for subview in theSubviews {
      subview.removeFromSuperview()
  }
Lunge answered 19/6, 2014 at 18:14 Comment(0)
F
2

For removing just subviews of a specific class - this was the only Swift code that worked for me in Xcode6.1.1. Assuming the only subviews you want to remove are of type UIButton...

for subView in nameofmysuperview.subviews {
    if subView.isKindOfClass(UIButton) {
        subView.removeFromSuperview()
    }
}
Frisette answered 5/3, 2015 at 21:55 Comment(0)
E
2

For Swift 3

I did as following because just removing from superview did not erase the buttons from array.

    for k in 0..<buttons.count {

      buttons[k].removeFromSuperview()

    }


    buttons.removeAll()
Ecclesia answered 22/3, 2017 at 4:48 Comment(0)
R
2

For Swift 4.+

extension UIView {
     public func removeAllSubViews() {
          self.subviews.forEach({ $0.removeFromSuperview() })

}

i hope this is use full for you.

Remuneration answered 2/11, 2018 at 8:44 Comment(0)
U
2

Here's a one-liner you've been looking for:

view.subviews.filter({$0.tag == 321}).forEach({$0.removeFromSuperview()})

Don't forget to Tag your view (someView.tag = 321) before you remove it with this code.

Unbrace answered 28/6, 2021 at 19:19 Comment(0)
L
1

One-liner:

while view.subviews.count > 0  { (view.subviews[0] as? NSView)?.removeFromSuperview() } 
Lauderdale answered 18/1, 2016 at 18:12 Comment(0)
S
1

Here's another approach that allows you call the operation on any collection of UIView instances (or UIView subclasses). This makes it easy to insert things like filter after .subviews so you can be more selective, or to call this on other collections of UIViews.

extension Array where Element: UIView {
  func removeEachFromSuperview() {
    forEach {
      $0.removeFromSuperview()
    }
  }
}

Example usage:

myView.subviews.removeEachFromSuperview()

// or, for example:

myView.subivews.filter { $0 is UIImageView }.removeEachFromSuperview()

Alternatively you can accomplish the same thing with a UIView extension (though this can't be called on some arbitrary array of UIView instances):

extension UIView {
    func removeSubviews(predicate: ((UIView) -> Bool)? = nil) 
        subviews.filter(
            predicate ?? { _ in true }
        ).forEach {
            $0.removeFromSuperview()
        }
    }
}

Example usage:

myView.removeSubviews()
myView.removeSubviews { $0 is UIImageView }
Spondylitis answered 23/2, 2021 at 4:31 Comment(0)
S
-3

did you try something like

for o : AnyObject in self.subviews {
     if let v = o as? NSView {
         v.removeFromSuperview()
     }
}
Septuagesima answered 19/6, 2014 at 17:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.