97

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?

robola
  • 975
  • 1
  • 6
  • 9
  • 8
    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. – aehlke Aug 01 '16 at 04:11
  • 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. – CleverPatrick Aug 08 '16 at 15:09
  • 2
    The documentation seems to suggest that you only need to call `removeFromSuperview`. I'm only calling `removeFromSuperview` without any issues. – Adam Johns Jan 04 '17 at 21:59
  • Seems there is a problem with IOS12 by just calling removeArrangedSubview. It's not removing the view properly. Calling just removeFromSuperview works. – Seeler99 Sep 21 '18 at 18:16
  • Calling removeArrangedSubview is not sufficient to remove the target subview from view hierarchy, as iOS 15, this is still the case. – Jerry Tian May 13 '22 at 02:40

9 Answers9

109

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)
Max Desiatov
  • 5,087
  • 3
  • 48
  • 56
zurakach
  • 1,324
  • 1
  • 8
  • 14
  • 1
    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. – drekka Jun 26 '18 at 04:02
  • 8
    @drekka can you cite your source, or provide a link? The doc seems clear about not needing both calls. – Graham Perks Jun 26 '18 at 18:26
  • 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! – simonthumper May 09 '19 at 10:17
  • 2
    @simonthumper did it fix it? – Gil Birman Oct 01 '19 at 06:40
  • 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 :) – Deryck Lucian Apr 09 '20 at 18:40
40

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.

Changnam Hong
  • 1,669
  • 18
  • 29
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

inreflection7
  • 1,275
  • 1
  • 11
  • 15
14

To remove a arrangedSubview from a stackview is

// To remove it from the view hierarchy
   subView.removeFromSuperview()
Ajumal
  • 1,048
  • 11
  • 33
  • `removeFromSuperview()` is enough, according to Apple‘s documentation and as you can also easily test yourself. `removeArrangedSubview()` is simply not necessary. – Joachim Kurz Oct 06 '19 at 14:22
  • 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. – Jerry Tian May 13 '22 at 02:35
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.

Graham Perks
  • 23,007
  • 8
  • 61
  • 83
  • As I said in a comment above, it may appear to work, but can trigger crashes. So just using `removeFromSuperview()` is not recommended. – drekka Jun 26 '18 at 04:06
7

No, To remove specific view.

func removeArrangedSubview(_ view: UIView)

To remove all subviews

stackView.arrangedSubviews.forEach { $0.removeFromSuperview() }
General Grievance
  • 4,555
  • 31
  • 31
  • 45
Anit Kumar
  • 8,075
  • 1
  • 24
  • 27
  • 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. – Fab1n Jun 27 '23 at 08:57
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()
            }
        }
    }
}
Nike Kov
  • 12,630
  • 8
  • 75
  • 122
  • 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 correctly – Rich Tolley Mar 18 '20 at 09:57
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()
}
Kishore Kumar
  • 4,265
  • 3
  • 26
  • 47
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.

Fab1n
  • 2,103
  • 18
  • 32