4

I am hiding and unhiding dropDownView(UIView consisting of textfield and button) based on Segment tapped in Segment Control. Following is the view hierarchy :-

enter image description here

Following is the code which hides/unhides dropDownView :-

 private func animateView(view: UIStackView, toHidden hidden: Bool) {
    UIView.animate(withDuration: 0.25) 
    {
    let firstView = view.arrangedSubviews[0]
    firstView.isHidden = hidden
    }
    }
func segmentValueChanged(_ sender: UISegmentedControl) {
        let segmentSelected = segmentControl.selectedSegmentIndex
        switch segmentSelected {
        case 0:
            animateView(view: mainStackView, toHidden: true)
        case 1:
         animateView(view: mainStackView, toHidden: true)
        case 2:
            animateView(view: mainStackView, toHidden: true)
        case 3:
           animateView(view: mainStackView, toHidden: false)
        default:
            break
        }
    }

Problem which I am facing is after changing segments over 10-15 times the above code stops working and DropDown View overlaps with Segment Control and I am not sure why. Any help to understand this issue is appreciated.

Also I have already tried
1. setNeedsLayout,
2. setNeedsDisplay and
3. reducing priority to height constraint of dropDownView to 999 from 1000

Meet
  • 1,196
  • 12
  • 26
  • 3
    I have noticed that repeatedly hiding or unhiding a view in a stack view seems to be cumulative. Try adding a check for isHidden and only change state if needed: `if view.isHidden { view.isHidden = false }` or `if !view.isHidden { view.isHidden = true }` – Chris May 19 '18 at 11:16
  • The view is mainStackView. So you mean I need to take outlet for dropDown view and check whether its hidden and then apply ? @Chris – Meet May 19 '18 at 11:30
  • Yes I think so - there may be another problem, but this worked for me in a simpler context of hiding and unhiding arranged subviews in a stack view in a tableview cell. – Chris May 19 '18 at 11:33
  • Also your first 3 cases could be represented as `case 0, 1, 2:` – Chris May 19 '18 at 11:36
  • yea.Will do that. However your suggestion didn't work. After 12 tries it doesn't work again. Thanks for case suggestion. – Meet May 19 '18 at 11:37
  • Ah ok. I have just submitted an answer including that idea. Could you try it without animating as ‘UIStackView’ should animate `isHidden` changes anyway? – Chris May 19 '18 at 11:48

5 Answers5

2

Seem the additional setting isHidden in completionClosure fixes that issue (Swift 5 syntax):

private func animateView(view: UIStackView, toHidden hidden: Bool) {
    let firstView = view.arrangedSubviews[0]
    UIView.animate(withDuration: 0.25) {
        firstView.isHidden = hidden
        view.layoutIfNeeded()
    } completion {
        firstView.isHidden = hidden
    }
}
Vadim Zhuk
  • 334
  • 2
  • 10
1

I've noticed that sometimes when you hide an arrangedSubview it doesn't hide its subviews, so here is the solution (or rather workaround) that works for me (firstView and secondView here are arrangedSubviews of some UIStackView):

firstView.isHidden = false
firstView.subviews.forEach { $0.isHidden = false }

secondView.isHidden = true
secondView.subviews.forEach { $0.isHidden = true }
0

Try without your animate function as stackView should animate hiding and unhiding anyway. Add a check for hidden status and only change it if necessary:

func segmentValueChanged(_ sender: UISegmentedControl) {
    let segmentSelected = segmentControl.selectedSegmentIndex
    let dropDown = mainStackView.arrangedSubviews.first!

    switch segmentSelected {
        case 0, 1, 2:
            if !dropDown.isHidden {
                dropDown.isHidden = true
            }
        case 3:
            if dropDown.isHidden {
                dropDown.isHidden = false
            }
        default:
            break
    }

}
Chris
  • 4,009
  • 3
  • 21
  • 52
  • That was already working. I needed to animate the behaviour of drop down hide and unhide. UIView animation as well as transition didn't work. StackView is not animating that behaviour. – Meet May 19 '18 at 11:49
  • Ok, sorry. I’ll need to think about this one :) – Chris May 19 '18 at 11:57
  • Np. You don't have to say sorry. – Meet May 19 '18 at 12:06
0

Try this one below. Hope will solve your problem.

private func animateView(view: UIStackView, toHidden hidden: Bool) {
    let firstView = view.arrangedSubviews[0]
    UIView.animate(withDuration: 0.25) {
        firstView.isHidden = hidden
        view.layoutIfNeeded()
    }
}
Priyam Dutta
  • 702
  • 5
  • 19
  • everything works even my mentioned code. keep changing segment 15 times then see if it works then. – Meet May 20 '18 at 03:38
0

Following is the solution that I implemented which seems to work:-

private func animateView(view: UIStackView, toHidden hidden: Bool) {
        if !hidden
        {
          mainStackView.insertArrangedSubview(view, at: 0)
          view.isHidden = true
          UIView.animate(withDuration: 0.25, animations: {
            view.isHidden = false
          })
        } else {
            let firstView = mainStackView.arrangedSubviews[0]
            UIView.animate(withDuration: 0.25, animations: {
                if firstView == view {
                    firstView.isHidden = true
                }
            }, completion: { [weak self] _ in
                if firstView == view {
                    self?.mainStackView.removeArrangedSubview(firstView)
                    firstView.removeFromSuperview()
                }
            })
        }
    }

Creating DropDown View programmatically (Instead of creating in storyboard) and removing same on every completion. I am not marking this answer correct as its a workaround. I want to understand why storyboard dropDownView fails after 10-15 tries.

Meet
  • 1,196
  • 12
  • 26