1. 程式人生 > >iOS --- 在程式碼中使用NSLayoutConstraint新增AutoLayout的約束條件

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:1
                                                        constant:0];
NSLayoutConstraint *bottom = [NSLayoutConstraint constraintWithItem:_viewTooled1
                                                       attribute:NSLayoutAttributeBottom
                                                       relatedBy:NSLayoutRelationEqual
                                                          toItem:_viewMain
                                                       attribute:NSLayoutAttributeBottom
                                                      multiplier:1
                                                        constant:0];
NSLayoutConstraint *leading = [NSLayoutConstraint constraintWithItem:_viewTooled1
                                                       attribute:NSLayoutAttributeLeading
                                                       relatedBy:NSLayoutRelationEqual
                                                          toItem:_viewMain
                                                       attribute:NSLayoutAttributeLeading
                                                      multiplier:1
                                                        constant:0];
_widthViewTooled1 = [NSLayoutConstraint constraintWithItem:_viewTooled1
                                                 attribute:NSLayoutAttributeWidth
                                                 relatedBy:NSLayoutRelationEqual
                                                    toItem:nil
                                                 attribute:NSLayoutAttributeNotAnAttribute
                                                multiplier:1
                                                  constant:100];
[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:2.0 animations:^{
        _widthViewTooled1.constant = 300;
        [self.view layoutIfNeeded];
    } completion:^(BOOL finished) {
        [UIView animateWithDuration:2.0 animations:^{
            _widthViewTooled2.constant = 300;
            [self.view layoutIfNeeded];
        } completion:^(BOOL finished) {
        }];
    }];
}