Swift:从 stackView 中消失的视图

Swift: Disappearing views from a stackView

我的主故事板中的设置相当简单:

我有如下代码可以处理不断变化的视图活动视图:

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,而 1 是隐藏的。

如果我慢慢地改变视图,这似乎一开始是有效的,但如果我改变得更快一点,索引 1 处的视图似乎在点击几下后永久隐藏,导致索引 0 处的视图覆盖整个屏幕。我已经放置了一个显示问题的动画和下面故事板的屏幕截图。输出显示,当问题发生时,单击第一段时两个视图都保持隐藏状态。

谁能告诉我为什么会这样?这是一个错误,还是我没有做我应该做的事情?

非常感谢!

更新:我似乎能够通过按顺序转到第一 > 第二 > 第三 > 第二 > 第一段来可靠地重现问题。

据我所知,这种奇怪的行为是由动画持续时间引起的。如您所见,动画完成需要一秒钟,但如果您开始切换 segmentControl 的速度比这快,那么我认为这就是导致此行为的原因。

您应该做的是在调用该方法时停用用户交互性,然后在动画完成后重新启用它。

它应该看起来像这样:

func updateView(segment: String) {

    segmentControl.userInteractionEnabled = false
    UIView.animateWithDuration(1.0, animations: {
        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")")
    }, completion: {(finished: Bool) in
        segmentControl.userInteractionEnabled = true
    }
}

虽然这会阻止快速切换(您可能会认为这是一个缺点),但我知道解决此问题的唯一其他方法是完全删除动画。

检查堆栈视图和子视图的配置和自动布局约束,尤其是分段控件。

分段控件使堆栈视图的设置变得复杂,因此我将分段控件从堆栈视图中取出并设置其相对于主视图的约束。

由于堆栈视图中的分段控件,设置堆栈视图相对简单,这样您的代码才能正常工作。

重置堆栈视图的约束,使其位于分段控件下方并覆盖父视图的其余部分。在 Attributes Inspector 中,将 Alignment 设置为 Fill,将 Distribution 设置为 Fill Equally,将 Content Mode 设置为 Scale to Fill。

移除对子视图的约束并将其内容模式设置为缩放以填充。

在您的代码中调整 arrangedSubviews 的索引,它应该会自动运行。

最后,在尝试了这里的所有建议后,我仍然无法弄清楚为什么会这样,所以我联系了 Apple,后者让我提交错误报告。但是我确实找到了解决方法,首先取消隐藏两个视图,这解决了我的问题:

func updateView(segment: String) {
    UIView.animate(withDuration: 1) {
        self.stackView.arrangedSubviews[1].isHidden = false
        self.stackView.arrangedSubviews[2].isHidden = false
        if(segment == "First") {
            self.stackView.arrangedSubviews[2].isHidden = true
        } else {
            self.stackView.arrangedSubviews[1].isHidden = true
        }
    }
}

错误是在堆栈视图中隐藏和显示视图是累积的。奇怪的苹果错误。如果在堆栈视图中隐藏视图两次,则需要显示两次才能将其取回。如果显示三遍,则需要隐藏三遍才能真正隐藏它(假设它隐藏开始)。

这与使用动画无关。

因此,如果您在代码中执行类似的操作,仅隐藏可见的视图,您将避免此问题:

if !myView.isHidden {
    myView.isHidden = true
}

在 Dave Batton 的精彩回答的基础上,您还可以添加 UIView 扩展以使调用站点更简洁,IMO。

extension UIView {

    var isHiddenInStackView: Bool {
        get {
            return isHidden
        }
        set {
            if isHidden != newValue {
                isHidden = newValue
            }
        }
    }
}

然后你可以调用 stackView.subviews[someIndex].isHiddenInStackView = false 如果你在堆栈视图中有多个视图要管理而不是一堆 if 语句,这将很有帮助。