Ios Swift:从stackView中消失的视图

Ios Swift:从stackView中消失的视图,ios,iphone,swift,uistackview,Ios,Iphone,Swift,Uistackview,我在主情节提要中有一个相当简单的设置: 包含三个视图的堆栈视图 第一个视图具有固定高度,并包含一个线段控制器 其他两个视图没有任何限制,其想法是一次只有一个视图处于活动状态,从而填充可用空间 我的代码将处理更改的视图活动视图,如下所示: import Foundation import UIKit class ViewController : UIViewController { @IBOutlet weak var stackView: UIStackView! @IBOu

我在主情节提要中有一个相当简单的设置:

  • 包含三个视图的堆栈视图
  • 第一个视图具有固定高度,并包含一个线段控制器
  • 其他两个视图没有任何限制,其想法是一次只有一个视图处于活动状态,从而填充可用空间
我的代码将处理更改的视图活动视图,如下所示:

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")")
        }
    }
}
如您所见,当选择名为“First”的选项卡时,索引1处的子视图应显示,而索引2处的子视图应隐藏;当选择任何其他内容时,索引2处的子视图应显示,而索引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
    }
}

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

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

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

使用堆栈视图之外的分段控件,设置堆栈视图相对简单,这样代码就能正常工作

重置堆栈视图上的约束,使其位于分段控件下方并覆盖superview的其余部分。在“属性检查器”中,将“对齐”设置为“填充”,将“分布”设置为“均匀填充”,将“内容模式”设置为“按比例填充”

删除子视图上的约束,并将其内容模式设置为“按比例填充”


在你的代码中调整arrangedSubviews上的索引,它会自动工作。

最后,在尝试了这里的所有建议之后,我仍然无法理解为什么它会这样,所以我联系了苹果,苹果要求我提交一份bug报告。不过,我确实找到了解决方法,首先取消隐藏两个视图,解决了我的问题:

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 == false {
    myView.isHidden = true
}

IMO称,基于davebatton的漂亮回答,您还可以添加UIView扩展以使呼叫站点更干净

extension UIView {

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

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

对不起,Benjamin,这似乎仍然无法解决问题-感谢您的尝试@本,哈,真奇怪。我再看一眼谢谢,我刚刚更新了上面的内容,说如果我转到第一个>第二个>第三个>第二个>第一个片段,我可以复制它,如果这有帮助的话@Ben如果你按顺序慢慢点击它们(例如,点击之间的停顿时间超过一秒钟),它是否仍然会被复制?是的-没有任何区别。你说你的两个其他视图没有“限制”,你应该用限制设置它们。然后,如果希望它们不显示在视图中,请告诉它们隐藏。堆栈视图最酷的一点是,当你告诉某些东西隐藏在其中时,堆栈视图将重新调整以正确匹配其余视图。文本“First”实际上在动画底部可见,因此不会隐藏。是否确实已在段控件上设置了固定高度约束?@phix23固定高度位于包含段控件的堆栈视图的第一个子视图上。它被设置为高度=32。我也尝试过堆栈外视图,所以我不相信是这样。它还报告为隐藏在
打印
中,因此它肯定认为它是隐藏的(除非我可能需要等待动画完成后再检查?。@zsteed我的理解是,如果没有约束,视图应该自动填充堆栈视图?无论哪种方式,即使有0/0/0/0约束,问题仍然会发生。我相信这是因为代码由于某种原因无法取消隐藏第一个视图,但我不知道为什么。我想是的,当显示和隐藏视图时,StackView会自动处理。运气不好-在进入第一个>第二个>第三个>第二个>第一个之后,仍然不允许取消隐藏第一个视图。我在UITableViewCell中遇到了与UIStackView相同的问题。由于单元格被重用,当您快速滚动表格时,stackview可能会连续快速隐藏/取消隐藏组件。我的解决办法是