• iOS


    抛出问题:为何在用到用到constraint的动画时以下代码无法实现动画的功能 ,没有动画直接刷新UI跳到80

    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
    { 
        [UIView animateWithDuration:2.0 animations:^{
          
           self.blueViewH.constant = 80;
    
        }];
    }

    而我们直接使用frame的时候动画是可以实现的

    - (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;
    
    @end
    @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))

    指定宽度为父视图的 1/4.

    make.width.equalTo(superview).multipliedBy(0.25);
    键盘弹出动画
     
    - (void)dealloc {
        [[NSNotificationCenter defaultCenter] removeObserver:self];
    }
    
    - (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);
    }];

    用masony三控件等宽间距

    方法一和方法二都在 NSArray+MASAdditions 

    方法一:

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

    方法二

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

    项目中还没有用到(一般这些方法都是重写然后再调用super 方法 然后在写一些其他的操作)

    更新约束的方法 在view中

    setNeedsUpdateConstraints:告知需要更新约束,但是不会立刻开始
    updateConstraintsIfNeeded:告知立刻更新约束
    updateConstraints:系统更新约束

    在viewController 中

    - (void)updateViewConstraints ;
       ViewController的View在更新视图布局时,会先调用ViewController的updateViewConstraints 方法。
       我们可以通过重写这个方法去更新当前View的内部布局,而不用再继承这个View去重写-updateConstraints方法。我们在重写这个方法时,务必要调用 super 或者 调用当前View的 -updateConstraints 方法。
  • 相关阅读:
    IDEA 实用功能Auto Import:自动优化导包(自动删除、导入包)
    idea 设置主题
    MySql where 后面使用函数导致索引失效问题
    IDEA报错,注解标红,提示Cannot resolve symbol xxx
    分批更新list
    java.lang.ArithmeticException: Rounding necessary
    Java selenium通过JS直接进行赋值给日期框
    postman接口测试之获取响应数据
    Jenkins集成allure测试报告
    Jenkins配置邮件通知
  • 原文地址:https://www.cnblogs.com/junhuawang/p/6008536.html
Copyright © 2020-2023  润新知