使用故事板中设置的标识符获取对 NSLayoutConstraint 的引用

Get reference to NSLayoutConstraint using Identifier set in storyboard

我正在使用故事板设置按钮的约束。我在约束的属性中看到了一个选项 "Identifier"。

我想引用这个约束,在代码中改变它的值,移动一个对象。

我如何从这个标识符中获得对这个 NSLayoutContraint 的引用。

看了文档,是这样写的

@interface NSLayoutConstraint (NSIdentifier)
/* For ease in debugging, name a constraint by setting its identifier, which will be printed in the constraint's description.
 Identifiers starting with UI and NS are reserved by the system.
 */
@property (nullable, copy) NSString *identifier NS_AVAILABLE_IOS(7_0);

@end

所以我意识到这是为了调试目的。

如果我想得到它并使用它怎么办?我看到了这个link,但是没有给出满意的答案:How to get NSLayoutConstraint's identifier by Its pointer?

我假设您已经为按钮设置了插座,因此您可以参考它。所以首先,从您的按钮中检索视图的约束。然后遍历数组并在每次迭代中将每个约束的标识符 属性 与您在 Interface Builder 中输入的值进行比较。看起来您是在 Objective-C 中编码,所以下面是 Objective-C 代码示例。将 @"identifier" 更改为您在 Interface Builder 中设置的任何值。

NSArray *constraints = [button constraints];
int count = [constraints count];
int index = 0;
BOOL found = NO;

while (!found && index < count) {
    NSLayoutConstraint *constraint = constraints[index];
    if ( [constraint.identifier isEqualToString:@"identifier"] ) {
        //save the reference to constraint
        found = YES;
    }

    index++;
}

Swift3,

 let filteredConstraints = button.constraints.filter { [=10=].identifier == "identifier" }
 if let yourConstraint = filteredConstraints.first {
      // DO YOUR LOGIC HERE
 }

Swift 3

我写了一个快速的 NSView 扩展,可以很好地处理这个问题。

extension NSView {
    func constraint(withIdentifier: String) -> NSLayoutConstraint? {
        return self.constraints.filter { [=10=].identifier == withIdentifier }.first
    }
}

用法:

if let c = button.constraint(withIdentifier: "my-button-width") {
    // do stuff with c
}

对于在公共视图容器中调整一组按钮的大小,这是可行的。每个 subview/button 必须使用共同的标识符(例如 "height")。

@IBAction func btnPressed(_ sender: UIButton) {
    for button in self.btnView.subviews{
        for constraint in button.constraints{
            if constraint.identifier == "height"{
                constraint.constant = constraint.constant == 0 ? 30:0
            }
        }
    }
    UIView.animate(withDuration: 0.3) { () -> Void in
        self.view.layoutIfNeeded()
    }
}

我简化了搜索并在父视图的祖先列表中添加:

+ (NSLayoutConstraint *) findConstraintNamed:(NSString *)identifierTarget startWith:(UIView *)aView;    
{    
    // walk upward from item through ancestors  
    UIView *currentView = aView;            

    while (currentView != nil)    
    {    
        NSArray *constraints = [currentView constraints];    
        NSInteger foundConstraintIndex = [constraints indexOfObjectPassingTest:^BOOL(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {   
            return [((NSLayoutConstraint *)obj).identifier isEqualToString:identifierTarget];    
        }];


        if (foundConstraintIndex != NSNotFound)    
        {    
            return constraints[foundConstraintIndex];    
        }                

        // not found, so walk up the ancestor chain    
        currentView = currentView.superview;    
    }    

    return nil;  // not found anywhere in item or ancestors!  :(    
}

您可能希望将先前答案中提供的逻辑外推到扩展中。

extension UIView {
    /// Returns the first constraint with the given identifier, if available.
    ///
    /// - Parameter identifier: The constraint identifier.
    func constraintWithIdentifier(_ identifier: String) -> NSLayoutConstraint? {
        return self.constraints.first { [=10=].identifier == identifier }
    }
}

然后您可以使用以下方法在任何地方访问任何约束:

myView.constraintWithIdentifier("myConstraintIdentifier")

编辑: 只是为了好玩,使用上面的代码,这里有一个扩展,可以在所有子 UIView 中找到具有该标识符的所有约束。必须将函数更改为 return 数组而不是找到的第一个约束。

    func constraintsWithIdentifier(_ identifier: String) -> [NSLayoutConstraint] {
        return self.constraints.filter { [=11=].identifier == identifier }
    }

    func recursiveConstraintsWithIdentifier(_ identifier: String) -> [NSLayoutConstraint] {
        var constraintsArray: [NSLayoutConstraint] = []

        var subviews: [UIView] = [self]

        while !subviews.isEmpty {
            constraintsArray += subviews.flatMap { [=11=].constraintsWithIdentifier(identifier) }
            subviews = subviews.flatMap { [=11=].subviews }
        }

        return constraintsArray
    }