I have a fairly simple set up in my main storyboard:
I have code that will deal with the changing view active views as follows:
import Foundation import UIKit class ViewController : UIViewController { @IBOutlet weak var stackView: UIStackView! @IBOutlet weak var segmentController: UISegmentedControl! @IBAction func SegmentClicked(_ sender: AnyObject) { updateView(segment: sender.titleForSegment(at: sender.selectedSegmentIndex)!) } override func viewDidLoad() { updateView(segment: "First") } func updateView(segment: String) { UIView.animate(withDuration: 1) { if(segment == "First") { self.stackView.arrangedSubviews[1].isHidden = false self.stackView.arrangedSubviews[2].isHidden = true } else { self.stackView.arrangedSubviews[1].isHidden = true self.stackView.arrangedSubviews[2].isHidden = false } print("Updating views") print("View 1 is \(self.stackView.arrangedSubviews[1].isHidden ? "hidden" : "visible")") print("View 2 is \(self.stackView.arrangedSubviews[2].isHidden ? "hidden" : "visible")") } } }
As you can see, when the tab called 'First' is selected, the subview at index 1 should show, whilst 2 is hidden, and when anything else is selected, the subview at index 2 should show, whilst 1 is hidden.
This appears to work at first, if I go slowly changing views, but if I go a bit quicker, the view at index 1 seems to remain permanently hidden after a few clicks, resulting in the view at index 0 covering the whole screen. I've placed an animation showing the issue and a screenshot of the storyboard below. The output shows that when the problem happens, both views remain hidden when clicking on the first segment.
Can anybody tell me why this is happening? Is this a bug, or am I not doing something I should be?
Many thanks in advance!
Update: I seem to be able to reliably reproduce the issue by going to the First > Second > Third > Second > First segments in that order.
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, or set the view's isHidden property to true.
Best use of stack view is that if you want to set multiple controls allinged vertically or horizontally to each other you just add all of them in stack view. stackview will handle its allignment you just need to give frame contraints to stackView.
Overview. Stack views let you leverage the power of Auto Layout, creating user interfaces that can dynamically adapt to the device's orientation, screen size, and any changes in the available space. The stack view manages the layout of all the views in its arrangedSubviews property.
The bug is that hiding and showing views in a stack view is cumulative. Weird Apple bug. If you hide a view in a stack view twice, you need to show it twice to get it back. If you show it three times, you need to hide it three times to actually hide it (assuming it was hidden to start).
This is independent of using animation.
So if you do something like this in your code, only hiding a view if it's visible, you'll avoid this problem:
if !myView.isHidden { myView.isHidden = true }
Building on the nice answer by Dave Batton, you can also add a UIView extension to make the call site a bit cleaner, IMO.
extension UIView { var isHiddenInStackView: Bool { get { return isHidden } set { if isHidden != newValue { isHidden = newValue } } } }
Then you can call stackView.subviews[someIndex].isHiddenInStackView = false
which is helpful if you have multiple views to manage within your stack view versus a bunch of if statements.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With