UIStackView : Is it really necessary to call both removeFromSuperView and removeArrangedSubview to remove a subview?
Asked Answered
M

9

98

From the UIStackView Class Reference

In removeArrangedSubview:

To prevent the view from appearing on screen after calling the stack’s removeArrangedSubview: method, explicitly remove the view from the subviews array by calling the view’s removeFromSuperview method.

In arrangedSubview:

Whenever an arranged view’s removeFromSuperview method is called, the stack view removes the view from its arrangedSubview array

From these, it seems that calling just removeFromSuperview is enough to remove a subview and I've been using it like that without problems. I also confirmed the behavior by logging the count of the arrangedSubviews array when removeFromSuperview is called.

A lot of tutorials and comments here on S/O however, say to call both. Is there a reason for this? Or do people just do it because the documentation says so?

Muhammadan answered 30/5, 2016 at 12:28 Comment(5)
Not a proper answer for you, but an anecdote: I was only calling removeArrangedSubview without knowing I was supposed to also call removeFromSuperview, and it was indeed clearing the view from arrangedSubviews but the view was still appearing in subviews causing all kinds of confusing breakage. Adding the removeFromSuperview call (once I saw this question!) resolved it.Fabulous
I don't know the answer, either. But I was having an issue where all my subviews were not being removed after using RemoveArrangedSubview. After seeing this post, I added removeFromSuperview calls and now it works as expected.Ferritin
The documentation seems to suggest that you only need to call removeFromSuperview. I'm only calling removeFromSuperview without any issues.Pulsimeter
Seems there is a problem with IOS12 by just calling removeArrangedSubview. It's not removing the view properly. Calling just removeFromSuperview works.Economy
Calling removeArrangedSubview is not sufficient to remove the target subview from view hierarchy, as iOS 15, this is still the case.Duckett
Z
113

No, just call subview.removeFromSuperview()

/* Removes a subview from the list of arranged subviews without removing it as
 a subview of the receiver.
    To remove the view as a subview, send it -removeFromSuperview as usual;
 the relevant UIStackView will remove it from its arrangedSubviews list
 automatically.
 */
open func removeArrangedSubview(_ view: UIView)
Zippel answered 20/2, 2017 at 11:47 Comment(5)
Actually this is an incorrect interpretation. Just using removeFromSuperView() appears to work, but can cause weird crashes if you are adding and removing a lot of views. If you read the discussion part of the docs it's pretty clear you have to call both.Laurellaurella
@Laurellaurella can you cite your source, or provide a link? The doc seems clear about not needing both calls.Chemisorb
I can confirm this, I have crash reports from my app when calling removeFromSuperview() only. I will be releasing an update shortly which also adds removeArrangedSubview() and report back on whether this fixes it!Metaphrase
@Metaphrase did it fix it?Consuetudinary
I was removing 2 buttons from stackView with removeArrangedSubview and I still had them after remove (only the frame was modified). after using func removeArrangedView(_ view: UIView) { removeArrangedSubview(view) view.removeFromSuperview() } everything is good :)Boarfish
A
41

In iOS 12.0, You need to use

stackView.arrangedSubviews[index].removeFromSuperview()

If you use removeArrangedSubview, there is a bug where the view at the specified index removed, but the view I want to clear appears at CGPoint(x: 0, y: 0).

Hope this help someone.

Adrell answered 9/10, 2018 at 9:57 Comment(1)
Having this issue 2. Where abouts is the big log for this?Linoel
F
15

Hacking With Swift provides a pretty good example and explanation, using Web views in this case.

The reason is that you can remove something from a stack view's arranged subview list then re-add it later, without having to recreate it each time – it was hidden, not destroyed. We don't want a memory leak, so we want to remove deleted web views entirely. If you find your memory usage ballooning, you probably forgot this step!

https://www.hackingwithswift.com/read/31/4/removing-views-from-a-uistackview-with-removearrangedsubview

Furbish answered 1/2, 2017 at 14:59 Comment(1)
hackingwithswift was updated to also only recommend removeFromSuperviewBrezhnev
N
14

To remove a arrangedSubview from a stackview is

// To remove it from the view hierarchy
   subView.removeFromSuperview()
Nkrumah answered 30/1, 2019 at 8:26 Comment(2)
removeFromSuperview() is enough, according to Apple‘s documentation and as you can also easily test yourself. removeArrangedSubview() is simply not necessary.Brezhnev
Calling removeArrangedSubview() only, actually will leave the view alive in the view hierarchy. It is a bug cost hours to debug, only when by dumping and checking the view hierarchy, I found the removed subview, is still in the view hierarchy, which is weird.Duckett
C
8

You're right, just the call to removeFromSuperview is sufficient to have the view fully removed.

I suspect the reason for people putting both is because they run across the removeArrangedSubview documentation which seems to say both are needed. (And indeed, they are, if you call removeArrangedSubview and want the view really gone.)

The additional doc in arrangedSubviews is not seen by so many, so they don't realize removeArrangedSubview is, in this case, optional.

Chemisorb answered 29/3, 2017 at 18:8 Comment(1)
As I said in a comment above, it may appear to work, but can trigger crashes. So just using removeFromSuperview() is not recommended.Laurellaurella
M
8

No, To remove specific view.

func removeArrangedSubview(_ view: UIView)

To remove all subviews

stackView.arrangedSubviews.forEach { $0.removeFromSuperview() }
Mcpherson answered 3/2, 2020 at 13:31 Comment(1)
The 2 lines are using different subview removal methods, thus are not doing the same. In some iOS versions removeArrangedSubview is not correct. You need to consistently call removeFromSuperview, which is the single call that works consistently, according to Apple doc and what all the others write here.Gnosis
S
4

For removing i use this extension. Don't really know is it necessary to remove constraints. But maybe it would help.

extension UIStackView {
    func removeAllArrangedSubviews() {
        let removedSubviews = arrangedSubviews.reduce([]) { (allSubviews, subview) -> [UIView] in
            self.removeArrangedSubview(subview)
            return allSubviews + [subview]
        }

        for v in removedSubviews {
            if v.superview != nil {
                NSLayoutConstraint.deactivate(v.constraints)
                v.removeFromSuperview()
            }
        }
    }
}
Sliding answered 10/2, 2020 at 11:18 Comment(1)
This is fine as long as the view removed is destroyed immediately. But there is a potential bug if it is reused - all the internal constraint have been removed so it will likely not layout correctlyPearlpearla
N
2

I will suggest get arranged subviews then remove it like below code .

for view in self.stackView.arrangedSubviews{
      self.stackView.removeArrangedSubview(view)
      view.removeFromSuperview()
}
Nabob answered 11/12, 2020 at 6:19 Comment(0)
G
0

Simple answer: NO! (as previously stated by many others)

Longer answer:

// to remove "view" from the stack entirely 
// calls removeArrangedSubview()
view.removeFromSuperview() 
// to add "view" to the stack
// calls addSubview()
stackView.addArrangeSubview(view)

Please check out the explicit paragraph in Apple's documentation about this issue (Maintain consistency between the arranged views and subview): https://developer.apple.com/documentation/uikit/uistackview#1653129

Here is the important part:

The stack view ensures that its arrangedSubviews property is always a subset of its subviews property. Specifically, the stack view enforces the following rules: When the stack view adds a view to its arrangedSubviews array, it also adds that view as a subview, if it isn’t already. When a subview is removed from the stack view, the stack view also removes it from the arrangedSubviews array. Removing a view from the arrangedSubviews array doesn’t remove it as a subview. The stack view no longer manages the view’s size and position, but the view is still part of the view hierarchy, and is rendered on screen if it’s visible.

Gnosis answered 27/6, 2023 at 9:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.