UIStackView 隐藏/显示 arrangedSubview 的问题

4

我正在根据选定的分段控件隐藏和显示下拉视图(由文本字段和按钮组成的UIView)。以下是视图层次结构:

enter image description here

以下是隐藏/显示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
        }
    }

我面临的问题是,更改了10-15次段后,上述代码停止工作,并且DropDown View与Segment Control重叠,我不确定原因。希望能提供任何帮助来理解这个问题。

此外,我已经尝试过以下方法:
1. setNeedsLayout,
2. setNeedsDisplay 和
3. 将 dropDownView 的高度约束优先级从1000降低到999


3
我注意到在堆栈视图中反复隐藏或取消隐藏一个视图似乎是累积的。尝试添加一个检查isHidden的条件,并且只有在需要更改状态时才进行更改:if view.isHidden { view.isHidden = false } 或者 if !view.isHidden { view.isHidden = true } - Chris
视图是mainStackView。那么你的意思是我需要为dropDown视图获取outlet并检查它是否隐藏,然后应用?@Chris - Meet
是的,我认为是这样 - 可能还有其他问题,但在一个更简单的环境中,对于在表格视图单元格中隐藏和取消隐藏排列子视图的情况,这对我起作用了。 - Chris
您的前三个情况也可以表示为 case 0, 1, 2: - Chris
是的。我会这样做。然而,你的建议没有起作用。尝试了12次后它再次失败了。感谢你的建议。 - Meet
啊,好的。我刚刚提交了一个答案,其中包括这个想法。你可以尝试一下不使用动画,因为“UIStackView”应该会自动动画化isHidden的变化。 - Chris
5个回答

2

看起来在完成闭包中添加isHidden这个附加设置可以解决这个问题(Swift 5语法):

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

1

我注意到有时候隐藏arrangedSubview时,它并不会隐藏其子视图,因此这里有一个对我有效的解决方案(或者说是变通方法)(这里的firstViewsecondView是某个UIStackViewarrangedSubviews):

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

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

0

尝试不使用animate函数,因为stackView应该自动隐藏和显示动画。添加一个检查隐藏状态的条件,只有在必要时才更改它:

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
    }

}

那已经可以工作了。我需要动画下拉隐藏和显示的行为。UIView动画以及转换都不起作用。StackView不能对该行为进行动画处理。 - Meet
好的,抱歉。我需要考虑一下这个问题 :) - Chris
没关系。你不必道歉。 - Meet

0

尝试下面这个。希望能解决你的问题。

private func animateView(view: UIStackView, toHidden hidden: Bool) {
    let firstView = view.arrangedSubviews[0]
    UIView.animate(withDuration: 0.25) {
        firstView.isHidden = hidden
        view.layoutIfNeeded()
    }
}

所有东西都能正常运行,包括我提到的代码。请继续更改片段15次,然后查看是否可以正常工作。 - Meet

0
以下是我实现的解决方案,似乎可以工作:-
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()
                }
            })
        }
    }

以编程方式创建下拉视图(而不是在故事板中创建),并在每次完成后删除相同的视图。我不会将此答案标记为正确,因为它只是一个解决方法。我想了解为什么在尝试10-15次后,故事板下拉视图会失败。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接