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) {
}];
}];
}