ios 删除影响 UIView 的所有约束

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24418884/
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 01:06:04  来源:igfitidea点击:

Remove all constraints affecting a UIView

iosobjective-cuiviewconstraintsautolayout

提问by Senseful

I have a UIView which is placed on the screen via several constraints. Some of the constraints are owned by the superview, others are owned by other ancestors (e.g. perhaps the view property of a UIViewController).

我有一个 UIView,它通过几个约束放置在屏幕上。一些约束由超级视图拥有,其他约束由其他祖先拥有(例如,可能是 UIViewController 的视图属性)。

I want to remove all of these old constraints, and place it somewhere new using new constraints.

我想删除所有这些旧约束,并使用新约束将其放置在新的地方。

How can I do this without creating an IBOutlet for every single constraint and having to remember which view owns said constraint?

如何在不为每个约束创建 IBOutlet 并且不必记住哪个视图拥有所述约束的情况下执行此操作?

To elaborate, the naive approach would be to create a bunch of IBOutlets for each of the constraints, and would then involve calling code such as:

详细地说,天真的方法是为每个约束创建一堆 IBOutlets,然后将涉及调用代码,例如:

[viewA removeConstraint:self.myViewsLeftConstraint];
[viewB removeConstraint:self.myViewsTopConstraint];
[viewB removeConstraint:self.myViewsBottomConstraint];
[self.view removeConstraint:self.myViewsRightConstraint];

The problem with this code is that even in the simplest case, I would need to create 2 IBOutlets. For complex layouts, this could easily reach 4 or 8 required IBOutlets. Furthermore, I would need to ensure that my call to remove the constraint is being called on the proper view. For example, imagine that myViewsLeftConstraintis owned by viewA. If I were to accidentally call [self.view removeConstraint:self.myViewsLeftConstraint], nothing would happen.

这段代码的问题是,即使在最简单的情况下,我也需要创建 2 个 IBOutlets。对于复杂的布局,这可以轻松达到 4 或 8 个所需的 IBOutlets。此外,我需要确保在正确的视图上调用我删除约束的调用。例如,假设它myViewsLeftConstraintviewA. 如果我不小心调用了[self.view removeConstraint:self.myViewsLeftConstraint],什么都不会发生。

Note:The method constraintsAffectingLayoutForAxislooks promising, but is intended for debugging purposes only.

注意:constraintsAffectingLayoutForAxis方法看起来很有前景,但仅用于调试目的。



Update:Many of the answers I am receiving deal with self.constraints, self.superview.constraints, or some variant of those. These solutions won't work since those methods return only the constraints ownedby the view, not the ones affectingthe view.

更新:很多我收到处理问题的答案self.constraintsself.superview.constraints或一些人的变种。这些解决方案将不起作用,因为这些方法只返回视图拥有的约束,而不是影响视图的约束。

To clarify the problem with these solutions, consider this view hierarchy:

要阐明这些解决方案的问题,请考虑以下视图层次结构:

  • Grandfather
    • Father
      • Me
        • Son
        • Daughter
      • Brother
    • Uncle
  • 祖父
    • 父亲
        • 儿子
        • 女儿
      • 兄弟
    • 叔叔

Now imagine we create the following constraints, and always attach them to their nearest common ancestor:

现在假设我们创建以下约束,并始终将它们附加到它们最近的共同祖先:

  • C0: Me: same top as Son (owned by Me)
  • C1: Me: width = 100 (owned by Me)
  • C2: Me: same height as Brother (owned by Father)
  • C3: Me: same top as Uncle (owned by Grandfather)
  • C4: Me: same left as Grandfather (owned by Grandfather)
  • C5: Brother: same left as Father (owned by Father)
  • C6: Uncle: same left as Grandfather (owned by Grandfather)
  • C7: Son: same left as Daughter (owned by Me)
  • C0:我:与儿子同顶(归我所有)
  • C1:我:宽度= 100(归我所有)
  • C2:我:和哥哥一样高(父亲拥有)
  • C3:我:和叔叔同顶(祖父所有)
  • C4:我:和祖父一样左(祖父所有)
  • C5:兄弟:与父亲同左(父亲拥有)
  • C6:叔叔:与祖父同左(祖父所有)
  • C7:儿子:与女儿同左(归我所有)

Now imagine we want to remove all constraints affecting Me. Any proper solution should remove [C0,C1,C2,C3,C4]and nothing else.

现在假设我们要删除所有影响Me. 任何适当的解决方案都应该删除[C0,C1,C2,C3,C4],没有别的。

If I use self.constraints(where self is Me), I will get [C0,C1,C7], since those are the only constraints owned by Me. Obviously it wouldn't be enough to remove this since it is missing [C2,C3,C4]. Furthermore, it is removing C7unnecessarily.

如果我使用self.constraints(其中 self 是我),我会得到[C0,C1,C7],因为这些是我拥有的唯一约束。显然,删除它是不够的,因为它丢失了[C2,C3,C4]。此外,它正在C7不必要地删除。

If I use self.superview.constraints(where self is Me), I will get [C2,C5], since those are the constraints owned by Father. Obviously we cannot remove all these since C5is completely unrelated to Me.

如果我使用self.superview.constraints(其中 self 是我),我会得到[C2,C5],因为这些是父亲拥有的约束。显然我们不能删除所有这些,因为C5Me.

If I use grandfather.constraints, I will get [C3,C4,C6]. Again, we cannot remove all of these since C6should remain intact.

如果我使用grandfather.constraints,我会得到[C3,C4,C6]。同样,我们不能删除所有这些,因为它们C6应该保持完整。

The brute force approach is to loop over each of the view's ancestors (including itself), and seeing if firstItemor secondItemare the view itself; if so, remove that constraint. This will lead to a correct solution, returning [C0,C1,C2,C3,C4], and only those constraints.

蛮力方法是遍历每个视图的祖先(包括它自己),并查看视图本身是否firstItemsecondItem是否是视图本身;如果是这样,请删除该约束。这将导致正确的解决方案,返回[C0,C1,C2,C3,C4],并且只有那些约束。

However, I'm hoping there is a more elegant solution than having to loop through the entire list of ancestors.

但是,我希望有一个比遍历整个祖先列表更优雅的解决方案。

采纳答案by Senseful

The only solution I have found so far is to remove the view from its superview:

到目前为止,我找到的唯一解决方案是从其超级视图中删除视图:

[view removeFromSuperview]

This looks like it removes all constraints affecting its layout and is ready to be added to a superview and have new constraints attached. However, it will incorrectly remove any subviews from the hierarchy as well, and get rid of [C7]incorrectly.

这看起来像它删除了影响其布局的所有约束,并准备添加到超级视图并附加新的约束。但是,它也会错误地从层次结构中删除任何子视图,并错误地删除[C7]

回答by marchinram

This approach worked for me:

这种方法对我有用:

@interface UIView (RemoveConstraints)

- (void)removeAllConstraints;

@end


@implementation UIView (RemoveConstraints)

- (void)removeAllConstraints
{
    UIView *superview = self.superview;
    while (superview != nil) {
        for (NSLayoutConstraint *c in superview.constraints) {
            if (c.firstItem == self || c.secondItem == self) {
                [superview removeConstraint:c];
            }
        }
        superview = superview.superview;
    }

    [self removeConstraints:self.constraints];
    self.translatesAutoresizingMaskIntoConstraints = YES;
}

@end

After it's done executing your view remains where it was because it creates autoresizing constraints. When I don't do this the view usually disappears. Additionally, it doesn't just remove constraints from superview but traversing all the way up as there may be constraints affecting it in ancestor views.

完成执行后,您的视图将保持原样,因为它会创建自动调整大小约束。当我不这样做时,视图通常会消失。此外,它不仅从父视图中删除约束,而且会一直向上遍历,因为在祖先视图中可能存在影响它的约束。



Swift 4 Version

斯威夫特 4 版本

extension UIView {

    public func removeAllConstraints() {
        var _superview = self.superview

        while let superview = _superview {
            for constraint in superview.constraints {

                if let first = constraint.firstItem as? UIView, first == self {
                    superview.removeConstraint(constraint)
                }

                if let second = constraint.secondItem as? UIView, second == self {
                    superview.removeConstraint(constraint)
                }
            }

            _superview = superview.superview
        }

        self.removeConstraints(self.constraints)
        self.translatesAutoresizingMaskIntoConstraints = true
    }
}

回答by emdog4

You can remove all constraints in a view by doing this:

您可以通过执行以下操作删除视图中的所有约束:

self.removeConstraints(self.constraints)

EDIT: To remove the constraints of all subviews, use the following extension in Swift:

编辑:要删除所有子视图的约束,请在 Swift 中使用以下扩展:

extension UIView {
    func clearConstraints() {
        for subview in self.subviews {
            subview.clearConstraints()
        }
        self.removeConstraints(self.constraints)
    }
}

回答by Alexander Volkov

In Swift:

在斯威夫特:

import UIKit

extension UIView {

    /**
     Removes all constrains for this view
     */
    func removeConstraints() {

        let constraints = self.superview?.constraints.filter{
            
// Declaration
class func deactivate(_ constraints: [NSLayoutConstraint])

// Usage
NSLayoutConstraint.deactivate(yourView.constraints)
.firstItem as? UIView == self ||
// Declaration
func removeConstraints(_ constraints: [NSLayoutConstraint])`

// Usage
yourView.removeConstraints(yourView.constraints)
.secondItem as? UIView == self } ?? [] self.superview?.removeConstraints(constraints) self.removeConstraints(self.constraints) } }

回答by E-Riddie

There are two ways of on how to achieve that according to Apple Developer Documentation

根据Apple Developer Documentation,有两种方法可以实现这一目标

1. NSLayoutConstraint.deactivateConstraints

1. NSLayoutConstraint.deactivateConstraints

This is a convenience method that provides an easy way to deactivate a set of constraints with one call. The effect of this method is the same as setting the isActive property of each constraint to false. Typically, using this method is more efficient than deactivating each constraint individually.

这是一种方便的方法,提供了一种通过一次调用停用一组约束的简单方法。该方法的效果与将每个约束的isActive属性设置为false是一样的。通常,使用此方法比单独停用每个约束更有效。

private var customConstraints = [NSLayoutConstraint]()

private func activate(constraints: [NSLayoutConstraint]) {
    customConstraints.append(contentsOf: constraints)
    customConstraints.forEach { 
import UIKit

extension UIView {

    func removeConstraints() { removeConstraints(constraints) }
    func deactivateAllConstraints() { NSLayoutConstraint.deactivate(getAllConstraints()) }
    func getAllSubviews() -> [UIView] { return UIView.getAllSubviews(view: self) }

    func getAllConstraints() -> [NSLayoutConstraint] {
        var subviewsConstraints = getAllSubviews().flatMap { 
print("constraints: \(view.getAllConstraints().count), subviews: \(view.getAllSubviews().count)")
view.deactivateAllConstraints()
.constraints } if let superview = self.superview { subviewsConstraints += superview.constraints.compactMap { (constraint) -> NSLayoutConstraint? in if let view = constraint.firstItem as? UIView, view == self { return constraint } return nil } } return subviewsConstraints + constraints } class func getAllSubviews(view: UIView) -> [UIView] { return view.subviews.flatMap { [
extension UIView {
/**
 * Deactivates immediate constraints that target this view (self + superview)
 */
func deactivateImmediateConstraints(){
    NSLayoutConstraint.deactivate(self.immediateConstraints)
}
/**
 * Deactivates all constrains that target this view
 */
func deactiveAllConstraints(){
    NSLayoutConstraint.deactivate(self.allConstraints)
}
/**
 * Gets self.constraints + superview?.constraints for this particular view
 */
var immediateConstraints:[NSLayoutConstraint]{
    let constraints = self.superview?.constraints.filter{
        
+ (void)RemoveContraintsFromView:(UIView*)view 
    removeParentConstraints:(bool)parent 
    removeChildConstraints:(bool)child;
.firstItem as? UIView === self ||
+ (void)RemoveContraintsFromView:(UIView *)view 
    removeParentConstraints:(bool)parent 
    removeChildConstraints:(bool)child
{
    if (parent) {
        // Remove constraints between view and its parent.
        UIView *superview = view.superview;
        [view removeFromSuperview];
        [superview addSubview:view];
    }

    if (child) {
        // Remove constraints between view and its children.
        [view removeConstraints:[view constraints]];
    }
}
.secondItem as? UIView === self } ?? [] return self.constraints + constraints } /** * Crawls up superview hierarchy and gets all constraints that affect this view */ var allConstraints:[NSLayoutConstraint] { var view: UIView? = self var constraints:[NSLayoutConstraint] = [] while let currentView = view { constraints += currentView.constraints.filter { return
extension UIView {
  func removeAllConstraints() {
    var view: UIView? = self
    while let currentView = view {
      currentView.removeConstraints(currentView.constraints.filter {
        return 
[self.superview.constraints enumerateObjectsUsingBlock:^(__kindof NSLayoutConstraint * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        NSLayoutConstraint *constraint = (NSLayoutConstraint *)obj;
        if (constraint.firstItem == self || constraint.secondItem == self) {
            [self.superview removeConstraint:constraint];
        }
    }];
    [self removeConstraints:self.constraints];
}
.firstItem as? UIView == self || ##代码##.secondItem as? UIView == self }) view = view?.superview } } }
.firstItem as? UIView === self || ##代码##.secondItem as? UIView === self } view = view?.superview } return constraints } }
] + getAllSubviews(view: ##代码##) } } }
.isActive = true } } private func clearConstraints() { customConstraints.forEach { ##代码##.isActive = false } customConstraints.removeAll() } private func updateViewState() { clearConstraints() let constraints = [ view.leadingAnchor.constraint(equalTo: parentView.leadingAnchor), view.trailingAnchor.constraint(equalTo: parentView.trailingAnchor), view.topAnchor.constraint(equalTo: parentView.topAnchor), view.bottomAnchor.constraint(equalTo: parentView.bottomAnchor) ] activate(constraints: constraints) view.layoutIfNeeded() }

2. UIView.removeConstraints(Deprecated for >= iOS 8.0)

2. UIView.removeConstraints(已弃用 >= iOS 8.0)

When developing for iOS 8.0 or later, use the NSLayoutConstraint class's deactivateConstraints: method instead of calling the removeConstraints: method directly. The deactivateConstraints: method automatically removes the constraints from the correct views.

在为 iOS 8.0 或更高版本开发时,请使用 NSLayoutConstraint 类的deactivateConstraints: 方法,而不是直接调用 removeConstraints: 方法。DeactivateConstraints: 方法会自动从正确的视图中删除约束。

##代码##

Tips

提示

Using Storyboards or XIBs can be such a pain at configuring the constraints as mentioned on your scenario, you have to create IBOutlets for each ones you want to remove. Even so, most of the time Interface Buildercreates more trouble than it solves.

使用Storyboards 或XIBs 在配置您的场景中提到的约束时可能会很痛苦,您必须为要删除的每个约束创建 IBOutlets。即便如此,在大多数情况下,Interface Builder制造的麻烦多于解决的麻烦。

Therefore when having very dynamic content and different states of the view, I would suggest:

因此,当有非常动态的内容和不同的视图状态时,我建议:

  1. Creating your views programmatically
  2. Layout them and using NSLayoutAnchor
  3. Append each constraint that might get removed later to an array
  4. Clear them every time before applying the new state
  1. 以编程方式创建视图
  2. 布局它们并使用NSLayoutAnchor
  3. 将以后可能会被删除的每个约束附加到一个数组中
  4. 每次在应用新状态之前清除它们

Simple Code

简单代码

##代码##

References

参考

  1. NSLayoutConstraint
  2. UIView
  1. NSLayoutConstraint
  2. 界面视图

回答by Vasily Bodnarchuk

Details

细节

  • Xcode 10.2.1 (10E1001), Swift 5
  • Xcode 10.2.1 (10E1001),Swift 5

Solution

解决方案

##代码##

Usage

用法

##代码##

回答by eonist

Based on previous answers (swift 4)

基于以前的答案 (swift 4)

You can use immediateConstraints when you don't want to crawl entire hierarchies.

当您不想爬行整个层次结构时,您可以使用immediateConstraints。

##代码##

回答by Darkseal

I use the following method to remove all constraints from a view:

我使用以下方法从视图中删除所有约束:

.h file:

.h 文件:

##代码##

.m file:

.m 文件:

##代码##

You can also read this poston my blog to better understand how it works behind the hood.

您还可以在我的博客上阅读这篇文章,以更好地了解它的幕后工作原理。

If you need more granular control, I'd stronglyadvise switching to Masonry, a powerful framework class you could use whenever you need to properly handle constraints programmatically.

如果您需要更精细的控制,我强烈建议您切换到Masonry,这是一个强大的框架类,您可以在需要以编程方式正确处理约束时使用它。

回答by Guig

A Swift solution:

一个快速的解决方案:

##代码##

It's important to go through all the parents, since the constraints between two elements are holds by the common ancestors, so just clearing the superview as detailed in this answeris not good enough, and you might end up having bad surprise later on.

遍历所有父元素很重要,因为两个元素之间的约束由共同祖先持有,因此仅清除本答案中详述的超级视图还不够好,而且您以后可能会感到意外。

回答by Giang

With objectiveC

用objectiveC

##代码##