【问题标题】:Swift: Disappearing views from a stackViewSwift:从stackView中消失的视图
【发布时间】:2017-02-21 10:07:09
【问题描述】:

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

  • 包含三个视图的堆栈视图
  • 第一个视图具有固定高度并包含一个段控制器
  • 其他两个视图没有限制,其想法是一次只有一个处于活动状态,从而填满可用空间

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

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, whilst 1 is hidden .

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

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

非常感谢!

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

【问题讨论】:

  • 你说你的另外两个视图没有“限制”你应该用约束来设置它们。然后,当您希望它们不显示在视图中时,请告诉它们隐藏。堆栈视图最酷的地方在于,当您告诉要隐藏其中的内容时,堆栈视图将重新调整以正确适应剩余的视图。
  • 文本“First”实际上在您的动画底部可见,因此并未隐藏。您确定在段控件上设置了固定高度约束吗?
  • @phix23 固定高度位于堆栈视图的第一个子视图上,其中包含段控件。它设置为 height = 32。无论如何,我也尝试过从堆栈视图中进行,所以我不相信是这样。它还报告为对print 隐藏,因此它肯定认为它是隐藏的(除非我可能需要等待动画完成才能检查?)。
  • @zsteed 我的理解是,如果没有约束,视图应该自动填充堆栈视图?无论哪种方式,即使使用 0/0/0/0 约束,问题仍然存在。我相信这是因为代码由于某种原因无法取消隐藏第一个视图,但我不知道为什么。
  • 我想是的,StackView 会在您显示和隐藏视图时自动处理。

标签: ios iphone swift uistackview


【解决方案1】:

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

这与使用动画无关。

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

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

【讨论】:

  • 这是真的,它没有记录在任何地方。调试为什么我的视图没有显示在堆栈视图中让我发疯了。言语无法形容这个实现有多愚蠢!非常感谢,戴夫!
  • 你先生是神。
  • 我之前遇到过这个问题,但我想说这个问题在 Xcode 9.2 中已经修复。
  • 你是妈妈! / Είσαι η μάνα!
  • 这是一个非常烦人的错误,它一直在搞砸我的 UI,直到我找到这个答案。
【解决方案2】:

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

extension UIView {

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

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

【讨论】:

  • 这非常好用且易于使用。对于任何排列的视图,您只需将 isHidden 替换为 isHiddenInStackView
  • 非常简单的答案!
【解决方案3】:

最后,在尝试了这里的所有建议之后,我仍然无法弄清楚它为什么会这样,所以我联系了 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
        }
    }
}

【讨论】:

  • 我在 UITableViewCell 中的 UIStackView 也遇到了同样的问题。因为单元格被重复使用,所以当您快速滚动表格时,stackview 可能会快速连续隐藏/取消隐藏组件。我的解决方案是在 UITableViewCell 的prepareForReuse 方法中取消隐藏,然后仅在必要时隐藏。这似乎有效。可能会帮助其他人:)
【解决方案4】:

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

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

使用堆栈视图之外的分段控件,设置堆栈视图相对简单,以便您的代码能够正常工作。

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

移除对子视图的约束并将其内容模式设置为 Scale to Fill。

在您的代码中调整排列子视图的索引,它应该会自动运行。

【讨论】:

  • 不走运 - 在第一个 > 第二个 > 第三个 > 第二个 > 第一个之后,它仍然不允许第一个视图被取消隐藏。
【解决方案5】:

据我所知,这种奇怪的行为是由动画持续时间引起的。如您所见,动画完成需要一秒钟,但如果您开始切换 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
    }
}

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

【讨论】:

  • 对不起,本杰明,这似乎仍然无法解决问题 - 感谢您的尝试!
  • @Ben Huh,这很奇怪。我再看看
  • 谢谢,我刚刚在上面进行了更新,说如果我转到第一个 > 第二个 > 第三个 > 第二个 > 第一段,我可以重现它,如果有帮助的话!
  • 是的 - 没有任何区别。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-20
  • 1970-01-01
相关资源
最近更新 更多