IluTov
IluTov

Reputation: 6852

Getting an existing NSLayoutConstraint for the width?

I'm trying to animate a control in Cocoa with auto layout.

Now, I can set [[constraint animator] setConstant:newWidth];, which works. But how can I get the right constraint?

With [self constraints] you can get all the constraints, and in this case I can just select constraints[0], but the order of the constraints may vary.

How can I be certain I always have the right constraint? The constraints are set in Interface Builder. I have seen that you can add a IBOutlet to it, but it doesn't seem necessary.


My solution

Thanks, it worked great. I wrote a little category.


NSView+NSLayoutConstraintFilter.h

#import <Cocoa/Cocoa.h>

@interface NSView (NSLayoutConstraintFilter)
- (NSLayoutConstraint *)constraintForAttribute:(NSLayoutAttribute)attribute;
- (NSArray *)constaintsForAttribute:(NSLayoutAttribute)attribute;
@end

NSView+NSLayoutConstraintFilter.m

#import "NSView+NSLayoutConstraintFilter.h"

@implementation NSView (NSLayoutConstraintFilter)

- (NSArray *)constaintsForAttribute:(NSLayoutAttribute)attribute {
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"firstAttribute = %d", attribute];
    NSArray *filteredArray = [[self constraints] filteredArrayUsingPredicate:predicate];

    return filteredArray;
}

- (NSLayoutConstraint *)constraintForAttribute:(NSLayoutAttribute)attribute {
    NSArray *constraints = [self constaintsForAttribute:attribute];

    if (constraints.count) {
        return constraints[0];
    }

    return nil;
}

@end

Upvotes: 18

Views: 13062

Answers (3)

Sentry.co
Sentry.co

Reputation: 5569

I wrote a reusable extension: (Swift 4.1)

/**
 * Utils when working with constraints
 */
extension NSLayoutConstraint{
    /**
     * Returns all constraints of kinds
     * EXAMPLE: NSLayoutConstraint.ofKind(rect.immediateConstraints, kinds: [.width,.height]) //width, height
     */
    static func ofKind(_ constraints:[NSLayoutConstraint],kinds:[NSLayoutAttribute]) -> [NSLayoutConstraint]{
        return kinds.map { kind in
            return constraints.filter { constraint in
                return constraint.firstAttribute == kind
            }
        }.flatMap({$0})//flattens 2d array to 1d array
    }
}

Upvotes: 0

longkai
longkai

Reputation: 3698

Here is the swift 3 version tested on Xcode 8.2.1 and macOS 10.12.2.

The code shows how to get a button's width and height constraints, but you could filter whatever you want from NSLayoutAttribute enum.

let cons = signInButton.constraints.filter {
    $0.firstAttribute == NSLayoutAttribute.width || $0.firstAttribute == NSLayoutAttribute.height /// or other from `NSLayoutAttribute`
}
// do something with the constraints array, e.g.
NSLayoutConstraint.deactivate(cons)

Upvotes: 5

Sunil Pandey
Sunil Pandey

Reputation: 7102

Every contraint has an attribute [constraint firstAttribute] It returns an enum NSLayoutAttribute

typedef NS_ENUM(NSInteger, NSLayoutAttribute) {
    NSLayoutAttributeLeft = 1,
    NSLayoutAttributeRight,
    NSLayoutAttributeTop,
    NSLayoutAttributeBottom,
    NSLayoutAttributeLeading,
    NSLayoutAttributeTrailing,
    NSLayoutAttributeWidth,
    NSLayoutAttributeHeight,
    NSLayoutAttributeCenterX,
    NSLayoutAttributeCenterY,
    NSLayoutAttributeBaseline,

    NSLayoutAttributeNotAnAttribute = 0
};

so you can check NSLayoutAttributeWidth for width.

Sample code:

NSArray constraints = [self constraints];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"firstAttribute = %d", NSLayoutAttributeWidth];
NSArray *filteredArray = [constraints filteredArrayUsingPredicate:predicate];
if(filteredArray.count == 0){
      return nil;
}
NSLayoutConstraint *constraint =  [constraints objectAtIndex:0];

Upvotes: 40

Related Questions