iOS - 查找视图的顶部约束



我正在尝试在代码中找到视图的顶部约束。顶部约束是在情节提要中添加的,我不想使用 IBOutlet。

在下面的代码中记录第一个属性的值似乎总是返回 NSLayoutAttributeHeight 类型的约束。知道如何在代码中可靠地找到视图的顶部约束吗?

NSLayoutConstraint *topConstraint;
for (NSLayoutConstraint *constraint in self.constraints) {
    if (constraint.firstAttribute == NSLayoutAttributeTop) {
        topConstraint = constraint;
        break;
    }
}

与其遍历self.constraints,不如遍历self.superview.constraints

self.constraints仅包含与视图相关的约束(例如高度和宽度约束(。

下面是一个代码示例,如下所示:

- (void)awakeFromNib
{
  [super awakeFromNib];
  if (!self.topConstraint) {
    [self findTopConstraint];
  }
}
- (void)findTopConstraint
{
  for (NSLayoutConstraint *constraint in self.superview.constraints) {
    if ([self isTopConstraint:constraint]) {
      self.topConstraint = constraint;
      break;
    }
  }
}
- (BOOL)isTopConstraint:(NSLayoutConstraint *)constraint
{
  return  [self firstItemMatchesTopConstraint:constraint] ||
          [self secondItemMatchesTopConstraint:constraint];
}
- (BOOL)firstItemMatchesTopConstraint:(NSLayoutConstraint *)constraint
{
  return constraint.firstItem == self && constraint.firstAttribute == NSLayoutAttributeTop;
}
- (BOOL)secondItemMatchesTopConstraint:(NSLayoutConstraint *)constraint
{
  return constraint.secondItem == self && constraint.secondAttribute == NSLayoutAttributeTop;
}

我通常在 IB 中设置一个必需约束的identifier,然后在代码中找到它,如下所示 (Swift(:

if let index = constraints.index(where: { $0.identifier == "checkmarkLeftMargin" }) {
    checkmarkImageViewLeftMargin = constraints[index]
}

或 @Tim 维穆伦

checkmarkImageViewLeftMargin = constraints.first { $0.identifier == "checkmarkLeftMargin" }

基于@Igor答案,我更改了一个位 itemMatch 方法,以考虑当第一项或第二项不是 UIView 时。例如,当将 UIView 顶部约束到安全区域顶部时。

extension UIView {
    func findConstraint(layoutAttribute: NSLayoutConstraint.Attribute) -> NSLayoutConstraint? {
        if let constraints = superview?.constraints {
            for constraint in constraints where itemMatch(constraint: constraint, layoutAttribute: layoutAttribute) {
                return constraint
            }
        }
        return nil
    }
    func itemMatch(constraint: NSLayoutConstraint, layoutAttribute: NSLayoutConstraint.Attribute) -> Bool {
        let firstItemMatch = constraint.firstItem as? UIView == self && constraint.firstAttribute == layoutAttribute
        let secondItemMatch = constraint.secondItem as? UIView == self && constraint.secondAttribute == layoutAttribute
        return firstItemMatch || secondItemMatch
    }
}

使用 swift 和 UIView 扩展

extension UIView {
    func findConstraint(layoutAttribute: NSLayoutAttribute) -> NSLayoutConstraint? {
        if let constraints = superview?.constraints {
            for constraint in constraints where itemMatch(constraint: constraint, layoutAttribute: layoutAttribute) {
                return constraint
            }
        }
        return nil
    }
    func itemMatch(constraint: NSLayoutConstraint, layoutAttribute: NSLayoutAttribute) -> Bool {
        if let firstItem = constraint.firstItem as? UIView, let secondItem = constraint.secondItem as? UIView {
            let firstItemMatch = firstItem == self && constraint.firstAttribute == layoutAttribute
            let secondItemMatch = secondItem == self && constraint.secondAttribute == layoutAttribute
            return firstItemMatch || secondItemMatch
        }
        return false
    }
}

在 Xcode 的检查器中设定标识符。这就是它的用途。你的名字。如果这还不够,您可以创建IBOutlet。

我在 Swift 中写了一个小扩展:

extension UIButton {
    var topConstraints: [NSLayoutConstraint]? {
        return self.constraints.filter( { ($0.firstItem as? UIButton == self && $0.firstAttribute == .top) || ($0.secondItem as? UIButton == self && $0.secondAttribute == .top) })
    }
}

下面是基于 @Igor 方法的单行扩展方法:

extension UIView{
    func constraint(for layoutAttribute: NSLayoutConstraint.Attribute) -> NSLayoutConstraint? {
        return superview?.constraints.first { itemMatch(constraint: $0, layoutAttribute: layoutAttribute) }
    }
    private func itemMatch(constraint: NSLayoutConstraint, layoutAttribute: NSLayoutConstraint.Attribute) -> Bool {
        if let firstItem = constraint.firstItem as? UIView, let secondItem = constraint.secondItem as? UIView {
            let firstItemMatch = firstItem == self && constraint.firstAttribute == layoutAttribute
            let secondItemMatch = secondItem == self && constraint.secondAttribute == layoutAttribute
            return firstItemMatch || secondItemMatch
        }
        return false
    }
}

[ 我还制作了方法签名样式以匹配 Swift 3...5 样式,例如:

constraint(for:)而不是findConstraint(layoutAttribute:).]

 func topConstraint() -> NSLayoutConstraint? {
    return superview?.constraints.first {$0.firstAttribute == .top}
}

最新更新