ios UIStackView - 隐藏堆栈视图时的布局约束问题

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/33642888/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 08:12:56  来源:igfitidea点击:

UIStackView - layout constraint issues when hiding stack views

iosswiftuistackview

提问by JEL

My app has 2 screens:

我的应用程序有 2 个屏幕:

  1. TableViewVC (no stack views here)

  2. DetailVC (all the nested stack views here; please see link for picture: Nested StackViews Picture) -- Note, there are labels and images within these stack views.

  1. TableViewVC(这里没有堆栈视图)

  2. DetailVC(此处所有嵌套的堆栈视图;请参阅图片链接:嵌套的 StackViews 图片)--注意,这些堆栈视图中有标签和图像。

When you press a cell in the tableview, it passes the information from the TableViewVC to the DetailVC. The problem is with hiding the specific UIStackViews in the DetailVC.I want only 2 stack views out of the various ones in the DetailVC to be hidden as soon as the view loads. So I write this code in the DetailVC to accomplish this:

当您按下 tableview 中的单元格时,它会将信息从 TableViewVC 传递到 DetailVC。问题在于在 DetailVC 中隐藏特定的 UIStackViews。我只想在视图加载后立即隐藏 DetailVC 中各种堆栈视图中的 2 个堆栈视图。所以我在 DetailVC 中写了这段代码来完成这个:

override func viewDidLoad() {
    super.viewDidLoad()

    self.nameLabel.text = "John"

    self.summaryStackView.hidden = true
    self.combinedStackView.hidden = true
}

Everything looks great but Xcode give many warnings only at runtime. There are no warning in Storyboard when the app is not running. Please see link for picture of errors: Picture of Errors

一切看起来都不错,但 Xcode 只在运行时给出许多警告。当应用程序未运行时,Storyboard 中没有警告。请参阅错误图片链接:错误图片

Basically it's a lot of UISV-hiding, UISV-spacing, UISV-canvas-connection errors. These errors go away if I hide the same stack views in viewDidAppearbut then there is a flash of the stuff that was supposed to be hidden and then it hides. The user sees the the view briefly and then it hides which is not good.

基本上这是很多 UISV-hiding、UISV-spacing、UISV-canvas-connection 错误。如果我在其中隐藏相同的堆栈视图,这些错误就会消失,viewDidAppear但随后出现了应该隐藏的内容,然后它又隐藏了。用户会短暂地看到视图,然后将其隐藏起来,这是不好的。

Sorry for not being able to actually post pictures instead of links, still can't do so.

抱歉无法实际发布图片而不是链接,仍然无法发布。

Any suggestions on how to fix this? This is for an app I actually want to launch to the app store - it's my first so any help would be great!

对于如何解决这个问题,有任何的建议吗?这是针对我真正想在应用商店发布的应用程序 - 这是我的第一个应用程序,所以任何帮助都会很棒!

Edit/ Update 1:

编辑/更新 1:

I found a small work around with this code which I put inside the second screen called DetailVC:

我找到了一个关于这段代码的小工作,我把它放在名为 DetailVC 的第二个屏幕中:

// Function I use to delay hiding of views
func delay(delay: Double, closure: ()->()) {
    dispatch_after(
        dispatch_time(
            DISPATCH_TIME_NOW,
            Int64(delay * Double(NSEC_PER_SEC))
        ),
        dispatch_get_main_queue(), closure)
}

// Hide the 2 stack views after 0.0001 seconds of screen loading
override func awakeFromNib() {
    delay(0.001) { () -> () in
        self.summaryStackView.hidden = true
        self.combinedStackView.hidden = true
    }
}

// Update view screen elements after 0.1 seconds in viewWillAppear
override func viewWillAppear(animated: Bool) {
    super.viewWillAppear(animated)
    delay(0.1) { () -> () in
        self.nameLabel.text = "John"
    }
}

This gets rid of the warnings about layout constraints completely from Xcode.

这完全从 Xcode 中消除了有关布局约束的警告。

It's still not perfect because sometimes I see a glimpse of the views that are supposed to be hidden -- they flash really quick on the screen then disappear. This happens so quickly though.

它仍然不完美,因为有时我会瞥见应该隐藏的视图——它们在屏幕上快速闪烁然后消失。但这发生得如此之快。

Any suggestions as to why this gets rid of warnings? Also, any suggestions on how to improve this to work perfectly??? Thanks!

关于为什么这会消除警告的任何建议?另外,关于如何改进它以完美工作的任何建议???谢谢!

采纳答案by adamek

Have you tried this, calling the super after your changes?

你有没有试过这个,在你改变后调用超级?

override func viewWillAppear() {

    self.nameLabel.text = "John"

    self.summaryStackView.hidden = true
    self.combinedStackView.hidden = true

    super.viewWillAppear()
}

回答by Raphael

I had the same problem and I fixed it by giving the height constraints of my initially hidden views a priority of 999.

我遇到了同样的问题,我通过将最初隐藏视图的高度限制设置为 999 来解决它。

enter image description here

在此处输入图片说明

The problem is that your stackview applies a height constraint of 0 on your hidden view which conflicts with your other height constraint. This was the error message:

问题是您的堆栈视图在隐藏视图上应用了 0 的高度约束,这与您的其他高度约束相冲突。这是错误消息:

Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) 
(
    "<NSLayoutConstraint:0x7fa3a5004310 V:[App.DummyView:0x7fa3a5003fd0(40)]>",
    "<NSLayoutConstraint:0x7fa3a3e44190 'UISV-hiding' V:[App.DummyView:0x7fa3a5003fd0(0)]>"
)

Giving your height constraint a lower priority solves this problem.

给你的高度限制一个较低的优先级可以解决这个问题。

回答by Senseful

This is a known problem with hiding nested stack views.

这是隐藏嵌套堆栈视图的已知问题。

There are essentially 3 solutions to this problem:

这个问题基本上有3种解决方案:

  1. Change the spacing to 0, but then you'll need to remember the previous spacing value.
  2. Call innerStackView.removeFromSuperview(), but then you'll need to remember where to insert the stack view.
  3. Wrap the stack view in a UIView with at least one 999 constraint. E.g. Top, Leading, Trailing @ 1000, Bottom@999.
  1. 将间距更改为 0,但您需要记住之前的间距值。
  2. 调用innerStackView.removeFromSuperview(),但是您需要记住在哪里插入堆栈视图。
  3. 将堆栈视图包装在具有至少一个 999 约束的 UIView 中。例如顶部、领先、尾随@1000、底部@999​​。

The 3rd option is the best in my opinion. For more information about this problem, why it happens, the different solutions, and how to implement solution 3, see my answer to a similar question.

第三个选项在我看来是最好的。有关此问题的更多信息、发生原因、不同的解决方案以及如何实施解决方案 3,请参阅我对类似问题的回答

回答by Dinesh

You can use the removeArrangedSubview and removeFromSuperview property of UIStackView.

您可以使用 UIStackView 的 removeArrangedSubview 和 removeFromSuperview 属性。

In Objective-C :

在 Objective-C 中:

 [self.topStackView removeArrangedSubview:self.summaryStackView];
 [self.summaryStackView removeFromSuperview];

 [self.topStackView removeArrangedSubview:self.combinedStackView];
 [self.combinedStackView removeFromSuperview];

From Apple UIStackView Documentation:(https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIStackView_Class_Reference/#//apple_ref/occ/instm/UIStackView/removeArrangedSubview:)

来自 Apple UIStackView 文档:( https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIStackView_Class_Reference/#//apple_ref/occ/instm/UIStackView/removeArrangedSubview:)

The stack view automatically updates its layout whenever views are added, removed or inserted into the arrangedSubviews array.

每当视图被添加、删除或插入到排列的子视图数组中时,堆栈视图会自动更新其布局。

  • removeArrangedSubview: This method removes the provided view from the stack's arrangedSubviews array. The view's position and size will no longer be managed by the stack view. However, this method does not remove the provided view from the stack's subviews array; therefore, the view is still displayed as part of the view hierarchy.
  • removeArrangedSubview:此方法从堆栈的排列子视图数组中删除提供的视图。视图的位置和大小将不再由堆栈视图管理。但是,此方法不会从堆栈的 subviews 数组中删除提供的视图;因此,视图仍然显示为视图层次结构的一部分。

To prevent the view from appearing on screen after calling the stack's removeArrangedSubview: method, explicitly remove the view from the subviews array by calling the view's removeFromSuperview method, or set the view's hidden property to YES.

要防止在调用堆栈的 removeArrangedSubview: 方法后视图出现在屏幕上,请通过调用视图的 removeFromSuperview 方法从子视图数组中显式删除视图,或将视图的 hidden 属性设置为 YES。

回答by Berthier Lemieux

When the UIViewStack is hidden, the constraints automatically generated by the UIStackView will throw lots of UISV-hiding, UISV-spacing, UISV-canvas-connection warnings, if the UIStackView's spacing property has any value other than zero.

当 UIViewStack 隐藏时,UIStackView 自动生成的约束会抛出大量 UISV-hiding、UISV-spacing、UISV-canvas-connection 警告,如果 UIStackView 的间距属性有任何非零值。

This doesn't make much sense, it's almost certainly a framework bug. The workaround I use is to set the spacing to zero when hiding the component.

这没有多大意义,它几乎可以肯定是一个框架错误。我使用的解决方法是在隐藏组件时将间距设置为零。

if hideStackView {
    myStackView.hidden = true
    myStackView.spacing = CGFloat(0)
} else {
    myStackView.hidden = false
    myStackView.spacing = CGFloat(8)
}

回答by Jacob Jennings

I've found that nested UIStackViews show this behavior if you set the hidden property in ?Interface Builder?. My solution was to set everything to not hidden in ?Interface Builder?, and hide things in viewWillAppear selectively.

我发现如果您在 ?Interface Builder? 中设置隐藏属性,嵌套的 UIStackViews 会显示这种行为。我的解决方案是将所有内容设置为不隐藏在“Interface Builder”中,并有选择地隐藏 viewWillAppear 中的内容。

回答by Joe Susnick

So, this may only help 0.000001% of users but maybe this is a clips to bounds issue.

所以,这可能只帮助 0.000001% 的用户,但也许这是一个剪辑边界问题。

I ran into this recently when working with UICollectionViewCellI forgot to check clips to bounds on the view I was treating as my content view. When you create a UITableViewCellin IB it sets up a content view with clips to bounds as the default.

我最近在与UICollectionViewCell我一起工作时遇到了这个问题,我忘记检查剪辑到我作为内容视图处理的视图的边界。当您UITableViewCell在 IB 中创建 a 时,它会设置一个内容视图,其中剪辑到边界作为默认值。

Point is, depending on your situation you may be able to accomplish your intended effect using frames and clipping.

重点是,根据您的情况,您可以使用帧和剪辑来实现预期的效果。

回答by tigaris

I moved all UIStackView.hidden code from viewDidLoad to viewDidAppear and broken constraints problem went away. In my case all conflicting constraints were auto generated, so no way to adjust priorities.

我将所有 UIStackView.hidden 代码从 viewDidLoad 移动到 viewDidAppear 并且约束问题消失了。就我而言,所有冲突的约束都是自动生成的,因此无法调整优先级。

I also used this code to make it prettier:

我还使用此代码使其更漂亮:

UIView.animateWithDuration(0.5) {
    self.deliveryGroup.hidden = self.shipVia != "1"
}

EDIT:

编辑:

Also needed the following code to stop it from happening again when device is rotated:

还需要以下代码来阻止它在设备旋转时再次发生:

override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
    super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator)

    self.deliveryGroup.hidden = false

    coordinator.animateAlongsideTransition(nil) {
    context in
        self.deliveryGroup.hidden = self.shipVia != "1"
    }
}

回答by aqubi

I fixed it by putting the hide commands in traitCollectionDidChange.

我通过将隐藏命令放在 traitCollectionDidChange 中来修复它。

override func traitCollectionDidChange(previousTraitCollection: UITraitCollection?) {
    super.traitCollectionDidChange(previousTraitCollection)
    self.item.hidden = true
}

回答by Davyd Geyl

This error is not about hiding, but about ambiguous constraints. You must not have any ambiguous constraints in your view. If you add them programmatically you should exactly understand what constraints you add and how they work together. If you do not add them programmatically, but use storyboard or xib, which is a good place to start, make sure there are no constraint errors or warnings.

这个错误不是关于隐藏,而是关于不明确的约束。您的视图中不能有任何模糊的约束。如果您以编程方式添加它们,您应该完全了解您添加的约束以及它们如何协同工作。如果您不以编程方式添加它们,而是使用 storyboard 或 xib(这是一个很好的起点),请确保没有约束错误或警告。

UPD: You have a pretty complex structure of views there. Without seeing the constraints is hard to say what exactly is wrong. However, I would suggest to build you view hierarchy gradually adding views one by one and making sure there are no design-time/runtime warnings. Scroll view may add another level of complexity if you do not handle it correctly. Find out how to use constraints with a scroll view. All other timing hacks is not a solution anyway.

UPD:那里的视图结构非常复杂。没有看到约束就很难说到底是哪里出了问题。但是,我建议您逐步构建视图层次结构,逐个添加视图并确保没有设计时/运行时警告。如果您处理不当,滚动视图可能会增加另一层复杂性。了解如何在滚动视图中使用约束。无论如何,所有其他计时技巧都不是解决方案。

回答by Tim

Put your hide commands in viewWillLayoutSubviews() {}

把你的隐藏命令放在 viewWillLayoutSubviews() {}