Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift: Disappearing views from a stackView

Tags:

I have a fairly simple set up in my main storyboard:

  • A stack view which includes three views
  • The first view has a fixed height and contains a segment controller
  • The other two views have no restrictions, the idea being that only one will be active at a time and thus fill the space available

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!

Animation showing issue

Image of stack view in storyboard

Update: I seem to be able to reliably reproduce the issue by going to the First > Second > Third > Second > First segments in that order.

like image 573
Ben Avatar asked Oct 12 '16 14:10

Ben


People also ask

How do I delete a view in stackView?

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.

When should I use stackView?

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.

What is stackView in Swift?

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.


2 Answers

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 } 
like image 51
Dave Batton Avatar answered Sep 18 '22 08:09

Dave Batton


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.

like image 37
Sandy Chapman Avatar answered Sep 18 '22 08:09

Sandy Chapman