Swift remove subviews from superview

27k views Asked by At

i have a scrollView, and i added a refreshcontroll to it.

self.refreshControl = UIRefreshControl()
self.refreshControl.attributedTitle = NSAttributedString(string: "Frissítéshez húzzad! :)")
self.refreshControl.addTarget(self, action: "refresh:", forControlEvents: UIControlEvents.ValueChanged)
self.scrollView.addSubview(refreshControl)

in the refresh method i have to remove all subviews from the scrollview, then repopulate the scrollview..

self.refreshControl = UIRefreshControl()
self.refreshControl.attributedTitle = NSAttributedString(string: "Frissítéshez húzzad! :)")
self.refreshControl.addTarget(self, action: "refresh:", forControlEvents: UIControlEvents.ValueChanged)
self.scrollView.addSubview(refreshControl)

after i try to pull, my scrollview get new data but it's don't have anymore refreshcontroll. i think it is because when i removing subviews from my scrollview i also remove the refreshcontroll from it. (if i add the refreshcontroll again in my refresh method my scrollview will have refreshconroll again) But there is another problem. After refreshing my scrollview moving down.. i attached to pictures:

This is how i remove the subiews:

func refresh(sender:AnyObject)
{
    //remove all subviews from scrollview..
    let subViews = self.scrollView.subviews
    for subview in subViews{
        println("for removing...")
        subview.removeFromSuperview()
    }

    println("refresh called..")
    UIApplication.sharedApplication().applicationIconBadgeNumber = 0
    //remove all elements from the array
    tstFrames.removeAll(keepCapacity: false)
    //refresh data from webservice and adding it to tstFrames Array
    wsServiceFeedTst()
    //adding items to the scrollview from tstFramesArray
    addPosts()
    self.refreshControl.endRefreshing()

}

This is how scrollview look like before refreshing: enter image description here

An this is how it looks like after refreshing: enter image description here

could anybody help me about why is this moving down?

Thank you!

Thank you, this is the solution:

let subViews = self.scrollView.subviews
for subview in subViews{
    println("for removing...")
    if (subview is PostLineItem) {
        subview.removeFromSuperview()
    }
    else {
        println("not removing..")
    }
}
2

There are 2 answers

4
Lyndsey Scott On BEST ANSWER

By removing all subviews, you can also be removing subviews other than the ones you explicitly added, like the refresh view and layout constraints.

(And to answer your question from the comments, layout constraints can in fact be subviews. I explain how to remove all subviews except layout constraints here: https://stackoverflow.com/a/27281242/2274694.)

In general, I recommend changing your code to be more specific such that only the views you've added are removed. For example, you could add a tag to the subviews you add in your addPosts method:

post.tag = 1000

then remove only the subviews with that tag in refresh::

let subViews = self.scrollView.subviews
for subview in subViews{
    if subview.tag == 1000 {
        subview.removeFromSuperview()
    }
}

to ensure that you don't remove any subviews you haven't explicitly added yourself.

Edit: It turns out the OP's added subviews are all of custom type PostLineItem so the tags are unnecessary since we can just remove all the PostLineItems instead:

for subview in self.view.subviews {
    if subview is PostLineItem {
        subview.removeFromSuperview()
     }
}
0
masp On

I stumbled about this problem today as well and would like to share my solution. You can remove all subviews of a view (and their subviews) with the following UIView extension in swift. In addition I attached a solution for removing all constraints the same way.

extension UIView {

  // Recursive remove subviews and constraints
  func removeSubviews() {
    self.subviews.forEach({
        if !($0 is UILayoutSupport) {
            $0.removeSubviews()
            $0.removeFromSuperview()
        }
    })

  }

  // Recursive remove subviews and constraints
  func removeSubviewsAndConstraints() {
    self.subviews.forEach({
        $0.removeSubviewsAndConstraints()
        $0.removeConstraints($0.constraints)
        $0.removeFromSuperview()
    })
  }

}

The first solution will keep all contraints, see Lyndseys answer. Be aware that this solution removes the whole hierarchy below the UIView. If you only want to remove specific subviews exchange or extend like this:

if !($0 is UILayoutSupport) && !($0 is PostLineItem)