删除所有影响UIView的约束

Sen*_*ful 71 constraints objective-c uiview ios autolayout

我有一个UIView,通过几个约束放在屏幕上.一些约束由superview拥有,其他约束由其他祖先拥有(例如,可能是UIViewController的view属性).

我想删除所有这些旧约束,并使用新约束将其置于新的位置.

如果不为每个约束创建一个IBOutlet并且必须记住哪个视图拥有所述约束,我该怎么做呢?

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

[viewA removeConstraint:self.myViewsLeftConstraint];
[viewB removeConstraint:self.myViewsTopConstraint];
[viewB removeConstraint:self.myViewsBottomConstraint];
[self.view removeConstraint:self.myViewsRightConstraint];
Run Code Online (Sandbox Code Playgroud)

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

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


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

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

  • 祖父
    • 父亲
        • 儿子
        • 女儿
      • 哥哥
    • 叔叔

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

  • C0:我:和儿子一样(由我拥有)
  • C1:我:宽度= 100(由我拥有)
  • C2:我:和兄弟一样高(由父亲拥有)
  • C3:我:和叔叔一样(由祖父拥有)
  • C4:我:和祖父一样(由祖父拥有)
  • C5:兄弟:和父亲一样(由父亲拥有)
  • C6:叔叔:和祖父一样(由祖父拥有)
  • C7:儿子:和女儿一样(由我拥有)

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

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

如果我使用self.superview.constraints(自我就是我),我会得到[C2,C5],因为那些是父拥有的约束.显然我们无法删除所有这些因为C5完全无关Me.

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

蛮力方法是循环在每个视图的祖先(包括其本身)的,并且如果看到firstItemsecondItem是视图本身; 如果是这样,删除该约束.这将导致正确的解决方案,返回[C0,C1,C2,C3,C4],并且只有那些约束.

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

mar*_*ram 59

这种方法对我有用:

@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
Run Code Online (Sandbox Code Playgroud)

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

  • 我正在删除约束,所以我想删除其他约束以保持它到位后使用自动调整约束,在我的代码中没有该行,视图将消失 (3认同)
  • 这应该是官方的答案. (2认同)

Sen*_*ful 42

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

[view removeFromSuperview]
Run Code Online (Sandbox Code Playgroud)

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


emd*_*og4 42

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

[_cell.contentView removeConstraints:_cell.contentView.constraints];
Run Code Online (Sandbox Code Playgroud)

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

extension UIView {
    func clearConstraints() {
        for subview in self.subviews {
            subview.clearConstraints()
        }
        self.removeConstraints(self.constraints)
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 此解决方案的问题在于它仅删除此视图所拥有的约束(例如,其宽度和高度).它不会删除诸如前导和顶部约束之类的内容. (15认同)
  • 我还没有验证你上面的评论。但是,我相信这将与上述相同或更好,[NSLayoutConstraintdeactivateConstraints:self.constraints]; (2认同)
  • 这将遇到与原始解决方案相同的问题。`self.constraints` 只返回 `self` 拥有的约束,而不是所有影响 `self` 的约束。 (2认同)

Ale*_*kov 17

在Swift中:

import UIKit

extension UIView {

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

        let constraints = self.superview?.constraints.filter{
            $0.firstItem as? UIView == self || $0.secondItem as? UIView == self
        } ?? []

        self.superview?.removeConstraints(constraints)
        self.removeConstraints(self.constraints)
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 也许更正确的是比较身份`如果c.firstItem === self`而不是铸造`as?UIView`并用`==`检查相等性 (2认同)

E-R*_*die 16

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

1. NSLayoutConstraint.deactivateConstraints

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

// Declaration
class func deactivate(_ constraints: [NSLayoutConstraint])

// Usage
NSLayoutConstraint.deactivate(yourView.constraints)
Run Code Online (Sandbox Code Playgroud)

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

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

// Declaration
func removeConstraints(_ constraints: [NSLayoutConstraint])`

// Usage
yourView.removeConstraints(yourView.constraints)
Run Code Online (Sandbox Code Playgroud)

提示

使用Storyboards或XIBs在配置场景中提到的约束时会非常困难,您必须为要删除的每个约束创建IBOutlet.即便如此,大部分时间Interface Builder都会产生比解决方案更多的麻烦.

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

  1. 以编程方式创建视图
  2. 布局它们并使用NSLayoutAnchor
  3. 将可能稍后删除的每个约束附加到数组
  4. 在应用新状态之前每次清除它们

简单代码

private var customConstraints = [NSLayoutConstraint]()

private func activate(constraints: [NSLayoutConstraint]) {
    customConstraints.append(contentsOf: constraints)
    customConstraints.forEach { $0.isActive = true }
}

private func clearConstraints() {
    customConstraints.forEach { $0.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()
}
Run Code Online (Sandbox Code Playgroud)

参考

  1. NSLayoutConstraint
  2. 的UIView

  • 当心。指定内在内容大小的视图将由 iOS 自动添加 NSContentSizeLayoutConstraint。通过访问正常的 yourView.constraints 属性来删除它会破坏这些视图的自动布局。 (2认同)

Vas*_*huk 5

细节

  • Xcode 10.2.1 (10E1001),Swift 5

解决方案

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 { $0.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 { [$0] + getAllSubviews(view: $0) }
    }
}
Run Code Online (Sandbox Code Playgroud)

用法

print("constraints: \(view.getAllConstraints().count), subviews: \(view.getAllSubviews().count)")
view.deactivateAllConstraints()
Run Code Online (Sandbox Code Playgroud)