- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
[UIView animateWithDuration:2.0 animations:^{
CGRect size =self.blueView.frame;
size.size.height = 80;
self.blueView.frame = size;//注意 frame的更改会调用父控件的 layoutSubviews 更新UI
思考尝试解决
没有动画效果 系统直接刷新渲染了
我们 手动强制更新刷新UI 放到动画里面试一下:对NSLayoutConstraint的对象赋值之后调用layoutIfNeeded方法
注意:layoutIfNeeded方法只会刷新子控件,因此要使用必须通过它的父类
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
self.blueViewW.constant = 80; //frame/bound发生改变 (系统会对布局进行渲染 但是想做动画的话 需要如下自己执行渲染)
[UIView animateWithDuration:2.0 animations:^{
// 对布局进行渲染
[self.view layoutIfNeeded]; //layoutIfNeeded方法只会刷新子控件,因此要使用必须通过它的父类
// //动画的方式2(没有上面的常用)
// [UIView beginAnimations:nil context:nil];
// [UIView setAnimationRepeatCount:10];
// [self.view layoutIfNeeded];
// [UIView commitAnimations];
原理推测:
NSLayoutConstraint的本质就是在系统底层转换为frame。立刻渲染 要想动画需要把手动更新刷新UI 放到动画里 这样才能动画 要不然就没有动画直接刷新UI跳到80
一个view的frame或bounds发生变化时,系统会设置一个flag给这个view,当下一个渲染时机到来时系统会重新按新的布局来渲染视图;
因此我们调用layoutIfNeeded方法对"self.blueViewW.constant = 80;" 不等下一渲染时机了 只要有标记,就直接刷新 实现了动画
实例应用如下(AutoLayout/Masonry):
AutoLayout
这是自定义了一个view 里面放里一个蓝色的lineView
#import "CustomView.h"
#import "Masonry.h"
@interface CustomView ()
@property (weak, nonatomic) IBOutlet UILabel *label;
@property (weak, nonatomic) IBOutlet UIView *lineView;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *lineCenterConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *lineTopConstraint;
@implementation CustomView
+ (instancetype)loadCustomView{
return [[[NSBundle mainBundle]loadNibNamed:@"CustomView" owner:nil options:nil]firstObject];
//注意constraint是约束限制 constant是常数
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
//1.先更改约束 (y = kx + b b是常数 (constant) 基于中心的0 负数是减 整数是加)
self.lineCenterConstraint.constant = -self.frame.size.width/4;
self.lineTopConstraint.constant = 70;
//2.在动画中更改布局
[UIView animateWithDuration:1.0 animations:^{
self.lineView.transform = CGAffineTransformRotate(self.lineView.transform, M_PI);
[self layoutIfNeeded];//调用更改约束的view 的父视图的layoutIfNeeded 不要掉自己self.lineView
} completion:^(BOOL finished) {
// [UIView beginAnimations:nil context:nil];
// [UIView setAnimationRepeatCount:10];
// [self layoutIfNeeded];//调用更改约束的view 的父视图的layoutIfNeeded 不要掉自己self.lineView
// self.lineView.transform = CGAffineTransformRotate(self.lineView.transform, M_PI);
// [UIView commitAnimations];
第三方框架Masonry
// 设置约束 初次设置约束使用
(NSArray *)mas_makeConstraints
// 更改约束 更新mas_makeConstraints里面的约束使用
(NSArray *)mas_updateConstraints
// 重新设置约束 先移除所有约束,再新增约束
(NSArray *)mas_remakeConstraints
//width >= 200 && width <= 400
make.width.greaterThanOrEqualTo(@200);
make.width.lessThanOrEqualTo(@400)
约束的优先级
.priority允许你指定一个精确的优先级,数值越大优先级越高.最高1000.
.priorityHigh等价于 UILayoutPriorityDefaultHigh .优先级值为 750.
.priorityMedium介于高优先级和低优先级之间,优先级值在 250~750之间.
.priorityLow等价于 UILayoutPriorityDefaultLow , 优先级值为 250.
优先级可以在约束的尾部添加:
make.left.greaterThanOrEqualTo(label.mas_left).with.priorityLow();
make.top.equalTo(label.mas_top).with.priority(600);
center 中心
//使 make 的centerX和 centerY = button1
make.center.equalTo(button1)
//使make的 centerX = superview.centerX - 5, centerY = superview.centerY + 10 make.center.equalTo(superview).centerOffset(CGPointMake(-5, 10))
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
__weak typeof(self) weakSelf = self;
_textField = [UITextField new];
_textField.backgroundColor = [UIColor redColor];
[self.view addSubview:_textField];
[_textField mas_makeConstraints:^(MASConstraintMaker *make) {
//left,right,centerx,y 不能共存只能有其二
make.left.mas_equalTo(20);
// make.right.mas_equalTo(-60);
make.centerX.equalTo(weakSelf.view);
make.height.mas_equalTo(40);
make.bottom.mas_equalTo(0);
// 注册键盘通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillChangeFrameNotification:) name:UIKeyboardWillChangeFrameNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHideNotification:) name:UIKeyboardWillHideNotification object:nil];
- (void)keyboardWillChangeFrameNotification:(NSNotification *)notification {
// 获取键盘基本信息(动画时长与键盘高度)
NSDictionary *userInfo = [notification userInfo];
CGRect rect = [userInfo[UIKeyboardFrameBeginUserInfoKey] CGRectValue];
CGFloat keyboardHeight = CGRectGetHeight(rect);
CGFloat keyboardDuration = [userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
// 修改下边距约束
[_textField mas_updateConstraints:^(MASConstraintMaker *make) {
make.bottom.mas_equalTo(-keyboardHeight);
// 重新布局
[UIView animateWithDuration:keyboardDuration animations:^{
[self.view layoutIfNeeded];
- (void)keyboardWillHideNotification:(NSNotification *)notification {
// 获得键盘动画时长
NSDictionary *userInfo = [notification userInfo];
CGFloat keyboardDuration = [userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
// 修改为以前的约束(距下边距0)
[_textField mas_updateConstraints:^(MASConstraintMaker *make) {
make.bottom.mas_equalTo(0);
// 重新布局
[UIView animateWithDuration:keyboardDuration animations:^{
[self.view layoutIfNeeded];
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
[self.view endEditing:YES];
以上是动画 下面是用masony三控件等宽间距(xib 加约束的方式还没有找到合适的)
假设有多个View,我们需要对其尺寸做批量设置
NSValue *sizeValue = [NSValue valueWithCGSize:CGSizeMake(100, 50)];
[@[view1,view2,view3] mas_makeConstraints:^(MASConstraintMaker *make) {
make.size.equalTo(sizeValue);
array 的 mas_distributeViewsAlongAxis withFixedSpacing
变化的是控件的长度或宽度 间距不变
定义一个存放三个控件的数组NSArray *array;
array = @[greenView,redView,blueView];
数组里面的元素不能小于1个,要不会报错 views to distribute need to bigger than one
- (void)getHorizontalone
//方法一,array 的 mas_distributeViewsAlongAxis
* 多个控件固定间隔的等间隔排列,变化的是控件的长度或者宽度值
* @param axisType 轴线方向
* @param fixedSpacing 间隔大小
* @param leadSpacing 头部间隔
* @param tailSpacing 尾部间隔
// MASAxisTypeHorizontal 水平
// MASAxisTypeVertical 垂直
[arrayList mas_distributeViewsAlongAxis:MASAxisTypeHorizontal
withFixedSpacing:20
leadSpacing:5
tailSpacing:5];
[arrayList mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(60);
make.height.mas_equalTo(100);
array 的 mas_distributeViewsAlongAxis withFixedItemLength
控件size不变,变化的是间隙
// 竖直方向高度不变60 宽度左边右边20 间距变
- (void)getVertical
* 多个固定大小的控件的等间隔排列,变化的是间隔的空隙
* @param axisType 轴线方向MASAxisTypeVertical 这时候withFixedItemLength是固定高度
* @param fixedItemLength 每个控件的固定长度或者宽度值
* @param leadSpacing 头部间隔
* @param tailSpacing 尾部间隔
[arrayList mas_distributeViewsAlongAxis:MASAxisTypeVertical
withFixedItemLength:60
leadSpacing:40
tailSpacing:10];
[arrayList mas_makeConstraints:^(MASConstraintMaker *make) {
// make.top.mas_equalTo(100);
// make.height.mas_equalTo(100);
make.left.mas_equalTo(20);
make.right.mas_equalTo(-20);
方法三 :直接设置multiplier
实现等间距
for (NSUInteger i = 0; i < 4; i++) {
UIView *itemView = [self getItemViewWithIndex:i];
[_containerView addSubview:itemView];
[itemView mas_makeConstraints:^(MASConstraintMaker *make) {
make.width.and.height.equalTo(@(ITEM_SIZE));
make.centerY.equalTo(_containerView.mas_centerY);
make.centerX.equalTo(_containerView.mas_right).multipliedBy(((CGFloat)i + 1) / ((CGFloat)ITEM_COUNT + 1));
方法四: 利用透明等宽度的SpaceView实现等间距
UIView *lastSpaceView = [UIView new];
lastSpaceView.backgroundColor = [UIColor greenColor];
[_containerView1 addSubview:lastSpaceView];
[lastSpaceView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.and.top.and.bottom.equalTo(_containerView1);
for (NSUInteger i = 0; i < ITEM_COUNT; i++) {
UIView *itemView = [self getItemViewWithIndex:i];
[_containerView1 addSubview:itemView];
[itemView mas_makeConstraints:^(MASConstraintMaker *make) {
make.height.and.width.equalTo(@(ITEM_SIZE));
make.left.equalTo(lastSpaceView.mas_right);
make.centerY.equalTo(_containerView1.mas_centerY);
UIView *spaceView = [UIView new];
spaceView.backgroundColor = [UIColor greenColor];
[_containerView1 addSubview:spaceView];
[spaceView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(itemView.mas_right).with.priorityHigh(); // 降低优先级,防止宽度不够出现约束冲突
make.top.and.bottom.equalTo(_containerView1);
make.width.equalTo(lastSpaceView.mas_width);
lastSpaceView = spaceView;
[lastSpaceView mas_makeConstraints:^(MASConstraintMaker *make) {
make.right.equalTo(_containerView1.mas_right);
setNeedsUpdateConstraints:告知需要更新约束,但是不会立刻开始
updateConstraintsIfNeeded:告知立刻更新约束
updateConstraints:系统更新约束
在viewController 中
- (void)updateViewConstraints ;
ViewController的View在更新视图布局时,会先调用ViewController的updateViewConstraints 方法。
我们可以通过重写这个方法去更新当前View的内部布局,而不用再继承这个View去重写-updateConstraints方法。我们在重写这个方法时,务必要调用 super 或者 调用当前View的 -updateConstraints 方法。