Meet
Meet

Reputation: 1206

UIStackView Hiding/UnHiding arrangedSubView issue

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

Upvotes: 4

Views: 5647

Answers (5)

Sergii Kostanian
Sergii Kostanian

Reputation: 51

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 }

Upvotes: 1

Vadim Zhuk
Vadim Zhuk

Reputation: 393

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
    }
}

Upvotes: 2

Meet
Meet

Reputation: 1206

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.

Upvotes: 0

Priyam Dutta
Priyam Dutta

Reputation: 702

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()
    }
}

Upvotes: 0

Chris
Chris

Reputation: 4411

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
    }

}

Upvotes: 0

Related Questions