What is the correct way to remove a subview from a view hierarchy and nuke it?
Asked Answered
P

8

40

I have a parent UIView with a number of subviews. Periodically I need to remove a subview and completely remove it from the system. What is the correct way to do this? I tried this:

UIView *v = [self.containerView viewWithTag:[n integerValue]];

[v removeFromSuperview];

and got a bizarre result. Previously present UIViews disappeared as well. What's going on?

Perseus answered 4/10, 2009 at 12:28 Comment(0)
S
72

Try this:

UIView *v = [self.containerView viewWithTag:[n integerValue]];
v.hidden = YES;
[self.containerView bringSubviewToFront:v];
[v removeFromSuperview];

Another thing I just noticed from the UIView class document - see the last sentence:

removeFromSuperview Unlinks the receiver from its superview and its window, and removes it from the responder chain.

  • (void)removeFromSuperview

Discussion If the receiver’s superview is not nil, this method releases the receiver. If you plan to reuse the view, be sure to retain it before calling this method and be sure to release it as appropriate when you are done with it or after adding it to another view hierarchy.

Never invoke this method while displaying.

UPDATE: It is now 2014 and removing a subview without hiding it works perfectly fine. The original poster's code should work as-is:

UIView *v = [self.containerView viewWithTag:[n integerValue]];
[v removeFromSuperview];

This will remove v and any views it has attached to it as subviews, leaving behind containerView and any siblings of v.

Steamer answered 6/10, 2009 at 6:23 Comment(3)
ominous Never invoke this method while displaying.Wahoo
couldn't be easier, just do this as Blackberry says for (UIView *s in [view subviews]) [s removeFromSuperview];Accompany
Back in '09, if you removed a subview before hiding it, you got some weird results. Or at least that's what I was seeing, and so was the original poster. And there used to be that warning in the UIView class reference, which has been replaced with another one. I'd say that the original poster's method of removing a subview is probably now correct and doesn't need any additional code. (he's just removing a single subview)Steamer
C
40

To remove all subviews from your view:

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

If you want to remove some specific view only then:

for(UIView *subview in [view subviews]) {
  if([subview isKindOfClass:[UIButton class]]) {
     [subview removeFromSuperview];
 } else {
     // Do nothing - not a UIButton or subclass instance
 }
}

You can also delete sub views by tag value:

for(UIView *subview in [view subviews]) {
    if(subview.tag==/*your subview tag value here*/) {
        [subview removeFromSuperview];

    } else {
        // Do nothing - not a UIButton or subclass instance
    }
}
Chengteh answered 3/8, 2012 at 6:43 Comment(9)
I like this better than the accepted answer. It seems like a much more direct approach - and you were pretty thorough including options for choosing whatever someone might want to delete. +1.Losing
hmm...actually now that I've tried to run this, it isn't working. I'm using the isKindOfClass option. I have UILabels and UISliders in my UIView, and I'm trying to delete all sliders, so I have: if ([subview isKindOfClass:[UISlider class]]); and it is returning true for everything, including the labels, so everything is getting deleted. I tried replacing isKindOfClass with isMemberOfClass, and that doesn't work either. Is there something special about UISliders that would make this not work?Losing
Figured out my problem, I had a typo that took me forever to find. The code works exactly as it is shown here.Losing
@Losing I Checked this and its working . In my view I Have two label,two slider and one button . and it deletes only sliders.for(UIView *subview in [self.view subviews]) { if([subview isKindOfClass:[UISlider class]]) { [subview removeFromSuperview]; } else { // Do nothing - not a UIButton or subclass instance } }Chengteh
Yeah, like I said, I had a typo that was messing it up. Your code is fine, I fixed the typo and it works great.Losing
what if I have multiple UIImageViews as subviews of the button, and only want to remove one particular?Gadson
add tag to each imageview and check tag value.Chengteh
Mike if it was just an irrelevant typo ... why not click Delete and delete the two confusing comments here ??Accompany
As GeneralMike says, accepted answer is bizarre. It's nothing more than for (UIView *s in [view subviews]) [s removeFromSuperview];Accompany
A
2

Swift 3.0:

let viewToRemove = mySuperView.viewWithTag(myTag)
viewToRemove?.removeFromSuperview()
Autry answered 13/3, 2017 at 23:42 Comment(0)
A
0

That's the right general idea. those other UIViews that disappear, what's their relationship to this UIView? Are they subviews of this view? Are they dealloc'd in the dealloc method of the view you're removing?

Are you sure your Tags are unique?

Sujal

Andrade answered 4/10, 2009 at 13:55 Comment(2)
All siblings to the view I'm trying to remove instantly dissappear from the screen. What the?Perseus
All siblings have unique tags, which is how I refer to the views.Perseus
A
0

Are they just disappearing from the display, or disappearing from the display and the view hierarchy? What does the debugger show you?

Ambroseambrosi answered 4/10, 2009 at 17:47 Comment(0)
K
0

Is it possible that cell.contentView has the same tag as the subview you want to remove? according to the documentation viewWithTag removes:

The view in the receiver’s hierarchy that matches tag. The receiver is included in the search.

If this is the case then you may be inadvertently removing cell.contentView from the cell. If n is zero and your cell's contentview has no tag set to it, it would default to 0 and cause that to happen.

Kizzee answered 6/10, 2009 at 6:2 Comment(0)
A
0

Swift 4: extend UIView

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

or

extension UIView {
    public func removeAllSubviews() {
        self.subviews.forEach { $0.removeFromSuperview() }
    }
}
Albany answered 21/6, 2018 at 9:39 Comment(0)
P
0
    override func viewWillAppear(_ animated: Bool)
    {
        super.viewWillAppear(animated)

        if let topController = UIApplication.topViewController() {

            if topController.isKind(of: ProviderHome.self)
            {
                let arrOfSuview = self.view.subviews

                if arrOfSuview.count > 1
                {
                    print("Davender Arr of subviews : \(arrOfSuview)")

                    for i in 0..<arrOfSuview.count
                    {
                        let objSub = arrOfSuview[i]

                        if objSub.tag == 101
                        {
                          objSub.removeFromSuperview()
                        }

                    }



                }


                NotificationCenter.default.addObserver(self, selector: #selector(ProviderHome.handelPushNotification), name: NSNotification.Name(rawValue: "handelPush"), object: nil)

                NotificationCenter.default.addObserver(self, selector: #selector(ProviderHome.handelLocalNotification), name: NSNotification.Name(rawValue: "handelLocal"), object: nil)
            }
        }


    }

@objc func handelPushNotification(_ notification: NSNotification)  {


        let arrOfSuview = self.view.subviews

        if arrOfSuview.count > 1
        {
            print("Davender Arr of subviews : \(arrOfSuview)")

            for i in 0..<arrOfSuview.count
            {
                let objSub = arrOfSuview[i]

                if objSub.tag == 101
                {
                    objSub.removeFromSuperview()
                }

            }

        }

        if notification.userInfo != nil
        {


            let dict = notification.userInfo as! Dictionary<String, Any>

            let d = dict["data"] as! Dictionary<String, Any>

            let action = d["gcm.notification.label"] as! String

            print("current message id :- ", action)

            self.getNotificationId = action

            if getNotificationId != ""
            {
               //call the api for getting Data

                AppDelegate.sharedInstance().myCurrentnotificationId = getNotificationId

                //working code
                let storyboard = UIStoryboard(name: "Provider", bundle: nil)
                let vc = storyboard.instantiateViewController(withIdentifier: "CommonPopUpsVC") as! CommonPopUpsVC
                vc.modalPresentationStyle = .overFullScreen
                vc.view.frame = self.view.frame
                vc.view.tag = 101
                self.view.addSubview(vc.view)
                self.present(vc, animated: true, completion: nil)

            }


       }
    }
Privity answered 3/7, 2019 at 6:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.