Получить ссылку на NSLayoutConstraint с помощью набора идентификаторов в раскадровке

Я устанавливал ограничения кнопки с помощью раскадровки. Я видел параметр "Идентификатор" в свойствах ограничения.

Screenshot of constraint's properties

Я хочу сделать ссылку на это ограничение, чтобы изменить его значение в коде, чтобы переместить объект.

Как я могу получить ссылку на этот NSLayoutContraint из этого идентификатора.

Я прочитал документацию, она была написана следующим образом

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

@end

Итак, я понял, что это для целей отладки.

Что делать, если я хочу его получить и использовать? Я видел эту ссылку, но не получил удовлетворительного ответа: Как получить идентификатор NSLayoutConstraint по указателю?

Ответ 1

Вы можете экстраполировать логику, представленную в предыдущем ответе, в расширение.

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 { $0.identifier == identifier }
    }
}

Затем вы можете получить доступ к любому ограничению в любом месте, используя:

myView.constraintWithIdentifier("myConstraintIdentifier")

Редактировать: просто для удовольствия, используя приведенный выше код, здесь расширение, которое находит все ограничения с этим идентификатором во всех дочерних UIViews. Пришлось изменить функцию, чтобы она возвращала массив вместо первого найденного ограничения.

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

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

        var subviews: [UIView] = [self]

        while !subviews.isEmpty {
            constraintsArray += subviews.flatMap { $0.constraintsWithIdentifier(identifier) }
            subviews = subviews.flatMap { $0.subviews }
        }

        return constraintsArray
    }

Ответ 2

В Swift 3,

 let filteredConstraints = button.constraints.filter { $0.identifier == "identifier" }
 if let yourConstraint = filteredConstraints.first {
      // DO YOUR LOGIC HERE
 }

Ответ 3

Swift 3

Я написал быстрое расширение NSView, которое прекрасно справляется с этим.

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

Использование:

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

Ответ 4

Я предполагаю, что у вас установлена ​​розетка для кнопки, поэтому у вас есть доступная ссылка на нее. Итак, сначала извлеките ограничения вида с вашей кнопки. Затем цикл через массив и на каждой итерации сравнивает свойство идентификатора каждого ограничения со значением, введенным вами в 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++;
}

Ответ 5

Я упростил поиск и добавил в список предков список предков:

+ (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!  :(    
}

Ответ 6

Для изменения размера набора кнопок в контейнере общего вида это работает. Каждая кнопка subview/должна использовать общий идентификатор (например, "высота" ).

@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()
    }
}