26

I'm trying to update a UIStackView so that a field displays, should the value of a UITextField equal "Other". Here is my code:

@IBOutlet var stackView: UIStackView!
func updateView() {
    print("UPDATING")
    UIView.animate(withDuration: 0.25, animations: { () -> Void in
         if(self.myTextField.text! == "Other") {
              print("SHOWING")
              self.stackView.arrangedSubviews[3].isHidden = false
         } else {
              print("HIDING")
              self.stackView.arrangedSubviews[3].isHidden = true
         }
         print("Is hidden: \(self.stackView.arrangedSubviews[3].isHidden )")
    })

An example output looks like this:

> UPDATING
> HIDING
> Is hidden: true
> UPDATING
> SHOWING
> Is hidden: true

As you can see, the isHidden attribute is reported as true, regardless of what the code above has set it to. I can't really figure out why that might be, but perhaps someone on here can? Is there something obvious to check? Is there any reason why isHidden can't be updated? (note there are no errors appearing in the output).

Ben
  • 4,707
  • 5
  • 34
  • 55
  • If I take your code sample plus your description literally it doesn’t work because of case sensitivity—“other” (as in your description) vs. “Other” (as in the sample. You might want to consider this: `self.myTextField.text!.lowercaseString == "other"`. – Tom E May 07 '17 at 12:56
  • Sorry, that's just a bad description, I'll update - see the output for proof there – Ben May 07 '17 at 12:57
  • @Ben did you figure it out? zisoft's solution removes the animation, so it is not good for me. If I remove the animation block, everything works as expected anyways. – Levi Nov 13 '17 at 13:34
  • @Levi I believe zisoft's answer did allow for animations (it certainly seems to looking at the code), however I don't actually have a working copy to prove it at this moment - sorry! – Ben Nov 14 '17 at 16:00
  • 1
    No problem, I used a workaround where I’m setting the items to shown/hidden also in the animation’s completion block, not just in the animation block I don’t like the solution, but it fixed the bug – Levi Nov 14 '17 at 16:11

4 Answers4

55

It's known UIStackView bug (http://www.openradar.me/25087688). There is a thread on SO about it: (Swift: Disappearing views from a stackView). Long story short:

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.

To fix this issue you can use following extension:

extension UIView {
    var isHiddenInStackView: Bool {
        get {
            return isHidden
        }
        set {
            if isHidden != newValue {
                isHidden = newValue
            }
        }
    }
}
Timur Bernikovich
  • 5,660
  • 4
  • 45
  • 58
  • 2
    I'm still seeing this issue in iOS 13. I have two labels in a StackView in a UITableViewCell and one or both of the labels should be hidden. Sometimes the labels were visible even though they were marked as hidden. I was only able to fix it by setting the labels to Hidden true in the storyboard and then using isHiddednInStackView to update their visibility. If they started out isHidden false and then had isHidden set to true they still were sometimes visible. Rotating the iPad could make labels in some rows become visible. – PhoneyDeveloper Mar 26 '20 at 17:29
7

Updates on the user interface always have to be done on the main thread (THE LAW).

So wrap you UI updates on the main thead:

@IBOutlet var stackView: UIStackView!
func updateView() {
    print("UPDATING")
    UIView.animate(withDuration: 0.25, animations: { () -> Void in
        DispatchQueue.main.async {  // UI updates on the main thread
            if(self.myTextField.text! == "Other") {
                print("SHOWING")
                self.stackView.arrangedSubviews[3].isHidden = false
             } else {
                print("HIDING")
                self.stackView.arrangedSubviews[3].isHidden = true
             }
             print("Is hidden: \(self.stackView.arrangedSubviews[3].isHidden )")
        }
    })
zisoft
  • 22,770
  • 10
  • 62
  • 73
  • Worked perfectly, thank you! Do you know why UIView.animate is not done on the main thread by default? How else would one use it? – Ben May 07 '17 at 13:02
  • Glad to help you. Please mark the answer as accepted. Animations are running on another thread to prevent the main thread from getting blocked. – zisoft May 07 '17 at 13:04
  • I can't until the timer has expired, but I of course will do so. I think that makes sense - so you can't change an isHidden property on anything but the main thread, but once those are changed, then the animation can run away from it. Thanks again! – Ben May 07 '17 at 13:07
  • 8
    I don't think it's the right solution, the duration of animation is ignored on the async. If you replace your 0.25sec to 3sec, you'll see that it doesn't work. – Ben Jul 13 '17 at 21:44
0

try to manipulate alpha along with isHidden property:

self.stackView.arrangedSubviews[3].isHidden = true
self.stackView.arrangedSubviews[3].alpha = 0

self.stackView.arrangedSubviews[3].isHidden = false
self.stackView.arrangedSubviews[3].alpha = 1
Anton Novoselov
  • 769
  • 2
  • 7
  • 19
0

This is still an active bug for iOS 16. So @Timur Bernikovich's solution is the only working solution. If you do not like to add any extension you need to check isHidden before the update. It does not help to run on the main thread since it will mess up the current animation (if you applied any animation for UIStackView). Therefore it's better to check the updating value before updating the view.isHidden value. As an example:

   var stackView: UIStackView!

    func isSectionActive(_ isActive: Bool) {
      UIView.animate(withDuration: 0.3) { [weak self] in
          self?.stackView.arrangedSubviews.forEach {
              // Active bug on UIStackView  http://www.openradar.me/25087688
              // So do not hide a subview twice
              if $0.isHidden != !isActive {
                  $0.isHidden = !isActive
                  $0.alpha = isActive ? 1.0 : 0.0
              }
          }
          self?.stackView.layoutIfNeeded()
      }
    }
Mudith Chathuranga Silva
  • 7,253
  • 2
  • 50
  • 58