天天看點

iOS --- 在代碼中使用NSLayoutConstraint添加AutoLayout的限制條件

AutoLayout是iOS開發中的布局适配神器。常在storyboard和xib檔案中直接使用, 用于不同螢幕大小的适配。而在某些情況下,需要使用代碼實作AutoLayout,則可以使用NSLayoutConstraint對象來添加限制條件。

NSLayoutConstraint對象

@interface NSLayoutConstraint : NSObject
{
    @private
    id _container;
    id _firstItem;
    id _secondItem;
    CGFloat _constant;
    CGFloat _loweredConstant;
    id _markerAndPositiveExtraVar;
    id _negativeExtraVar;
    float _coefficient;
    UILayoutPriority _priority;
    uint64_t _layoutConstraintFlags;
    id _flange;
}

/* Create an array of constraints using an ASCII art-like visual format string.
 */
+ (NSArray *)constraintsWithVisualFormat:(NSString *)format options:(NSLayoutFormatOptions)opts metrics:(NSDictionary *)metrics views:(NSDictionary *)views;

/* This macro is a helper for making view dictionaries for +constraintsWithVisualFormat:options:metrics:views:.  
 NSDictionaryOfVariableBindings(v1, v2, v3) is equivalent to [NSDictionary dictionaryWithObjectsAndKeys:v1, @"v1", v2, @"v2", v3, @"v3", nil];
 */
#define NSDictionaryOfVariableBindings(...) _NSDictionaryOfVariableBindings(@"" # __VA_ARGS__, __VA_ARGS__, nil)
UIKIT_EXTERN NSDictionary *_NSDictionaryOfVariableBindings(NSString *commaSeparatedKeysString, id firstValue, ...) NS_AVAILABLE_IOS(6_0); // not for direct use


/* Create constraints explicitly.  Constraints are of the form "view1.attr1 = view2.attr2 * multiplier + constant" 
 If your equation does not have a second view and attribute, use nil and NSLayoutAttributeNotAnAttribute.
 */
+(instancetype)constraintWithItem:(id)view1 attribute:(NSLayoutAttribute)attr1 relatedBy:(NSLayoutRelation)relation toItem:(id)view2 attribute:(NSLayoutAttribute)attr2 multiplier:(CGFloat)multiplier constant:(CGFloat)c;

/* If a constraint's priority level is less than UILayoutPriorityRequired, then it is optional.  Higher priority constraints are met before lower priority constraints.
 Constraint satisfaction is not all or nothing.  If a constraint 'a == b' is optional, that means we will attempt to minimize 'abs(a-b)'.
 This property may only be modified as part of initial set up.  An exception will be thrown if it is set after a constraint has been added to a view.
 */
@property UILayoutPriority priority;

/* When a view is archived, it archives some but not all constraints in its -constraints array.  The value of shouldBeArchived informs UIView if a particular constraint should be archived by UIView.
 If a constraint is created at runtime in response to the state of the object, it isn't appropriate to archive the constraint - rather you archive the state that gives rise to the constraint.  Since the majority of constraints that should be archived are created in Interface Builder (which is smart enough to set this prop to YES), the default value for this property is NO.
 */
@property BOOL shouldBeArchived;

/* accessors
 firstItem.firstAttribute {==,<=,>=} secondItem.secondAttribute * multiplier + constant
 */
@property (readonly, assign) id firstItem;
@property (readonly) NSLayoutAttribute firstAttribute;
@property (readonly) NSLayoutRelation relation;
@property (readonly, assign) id secondItem;
@property (readonly) NSLayoutAttribute secondAttribute;
@property (readonly) CGFloat multiplier;

/* Unlike the other properties, the constant may be modified after constraint creation.  Setting the constant on an existing constraint performs much better than removing the constraint and adding a new one that's just like the old but for having a new constant.
 */
@property CGFloat constant;

/* The receiver may be activated or deactivated by manipulating this property.  Only active constraints affect the calculated layout.  Attempting to activate a constraint whose items have no common ancestor will cause an exception to be thrown.  Defaults to NO for newly created constraints. */
@property (getter=isActive) BOOL active NS_AVAILABLE(10_10, 8_0);

/* Convenience method that activates each constraint in the contained array, in the same manner as setting active=YES. This is often more efficient than activating each constraint individually. */
+ (void)activateConstraints:(NSArray *)constraints NS_AVAILABLE(10_10, 8_0);

/* Convenience method that deactivates each constraint in the contained array, in the same manner as setting active=NO. This is often more efficient than deactivating each constraint individually. */
+ (void)deactivateConstraints:(NSArray *)constraints NS_AVAILABLE(10_10, 8_0);
@end

@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 (copy) NSString *identifier NS_AVAILABLE_IOS(7_0);

@end
           

對其中的屬性不多做解釋, 多留意下priority,multiplier, constant分别對應優先級, 倍數, 限制值。

constraintWithItem方法

該方法常用添加單個限制條件。

/* Create constraints explicitly.  Constraints are of the form "view1.attr1 = view2.attr2 * multiplier + constant" 
 If your equation does not have a second view and attribute, use nil and NSLayoutAttributeNotAnAttribute.
 */
+(instancetype)constraintWithItem:(id)view1 attribute:(NSLayoutAttribute)attr1 relatedBy:(NSLayoutRelation)relation toItem:(id)view2 attribute:(NSLayoutAttribute)attr2 multiplier:(CGFloat)multiplier constant:(CGFloat)c;
           

參數解釋如下:

- view1:應用該限制條件的UIView對象,

- attr1:限制參數(top,bottom,leading,training,width,height等)

- relation:相等或者其他關系

- view2:參考UIView

- attr2:參考UIView的參數

- multiplier:倍數

- c:限制值

即得到的限制為:

view1.attr1 = view2.attr2 * multiplier + constant

執行個體

_viewTooled1 = [[UIView alloc] init];
_viewTooled1.backgroundColor = [UIColor greenColor];
_viewTooled1.translatesAutoresizingMaskIntoConstraints = NO;
[_viewMain addSubview:_viewTooled1];

NSLayoutConstraint *top = [NSLayoutConstraint constraintWithItem:_viewTooled1
                                                       attribute:NSLayoutAttributeTop
                                                       relatedBy:NSLayoutRelationEqual
                                                          toItem:_viewMain
                                                       attribute:NSLayoutAttributeTop
                                                      multiplier:
                                                        constant:];
NSLayoutConstraint *bottom = [NSLayoutConstraint constraintWithItem:_viewTooled1
                                                       attribute:NSLayoutAttributeBottom
                                                       relatedBy:NSLayoutRelationEqual
                                                          toItem:_viewMain
                                                       attribute:NSLayoutAttributeBottom
                                                      multiplier:
                                                        constant:];
NSLayoutConstraint *leading = [NSLayoutConstraint constraintWithItem:_viewTooled1
                                                       attribute:NSLayoutAttributeLeading
                                                       relatedBy:NSLayoutRelationEqual
                                                          toItem:_viewMain
                                                       attribute:NSLayoutAttributeLeading
                                                      multiplier:
                                                        constant:];
_widthViewTooled1 = [NSLayoutConstraint constraintWithItem:_viewTooled1
                                                 attribute:NSLayoutAttributeWidth
                                                 relatedBy:NSLayoutRelationEqual
                                                    toItem:nil
                                                 attribute:NSLayoutAttributeNotAnAttribute
                                                multiplier:
                                                  constant:];
[self.view addConstraints:@[top, bottom, leading, _widthViewTooled1]];
           

以上執行個體,設定_viewTooled的top,bottom,leading都與_viewMain對齊,而其width則可以根據_widthViewTooled1限制條件來修改,甚至做成動畫形式。

constraintsWithVisualFormat

constraintsWithVisualFormat是另一個非常常用的限制方法,涉及到VFL這種特殊的文法規則,将留在下一篇部落格中詳細介紹。

需要注意的幾點

有如下需要注意的地方:

1. [_viewMain addSubview:_viewTooled1];一定要放在[self.view addConstraints:@[top, bottom, leading, _widthViewTooled1]];的前邊,否則添加限制失敗,編譯器報錯。

2. 在UIView動畫中,對constant進行了修改,則要使用layoutIfNeeded對布局進行重新整理,否則看不到動畫的過程。

- (void)viewToolAnimation {
    [UIView animateWithDuration: animations:^{
        _widthViewTooled1.constant = ;
        [self.view layoutIfNeeded];
    } completion:^(BOOL finished) {
        [UIView animateWithDuration: animations:^{
            _widthViewTooled2.constant = ;
            [self.view layoutIfNeeded];
        } completion:^(BOOL finished) {
        }];
    }];
}