iOS实现一个轻微晃动的提示动画

背景

我们需要用到一个上下轻微晃动的动画效果,提示有什么东西,吸引用户的注意。

方案一

- (UIButton *)moveBtn {
  if (!_moveBtn) {
   _moveBtn = [UIButton buttonWithType:UIButtonTypeCustom];
  _moveBtn.frame = CGRectMake(0,0 ,300,150);
   _moveBtn.enabled = NO;
    [_moveBtn setBackgroundImage:[UIImage imageNamed:@"yangmi"] forState:UIControlStateDisabled];
    [_moveBtn setTitleColor:[UIColor redColor] forState:(UIControlStateNormal)];
    _moveBtn.titleLabel.font = [UIFont systemFontOfSize:10];
    _moveBtn.layer.masksToBounds = YES;
    _moveBtn.layer.cornerRadius = 5.f;
    CGFloat width = [UIScreen mainScreen].bounds.size.width;
    CGFloat height = [UIScreen mainScreen].bounds.size.height;
    _moveBtn.layer.anchorPoint = CGPointMake(0, 1);
    //这句代码改变了上面原始的frame,这个时候可以设置position
    //也可以设置frame
    //_moveBtn.frame = CGRectMake(width / 2.f - 150,height / 2.f - 75 ,300,150);
    _moveBtn.layer.position = CGPointMake(width / 2.f - 150, height/2.f + 75);
    ///这个点设置很关键,可以设置摇动的锚点,同时让这个按钮屏幕居中
  }
  return _moveBtn;
}

- (void)shakeAnimationForViewQ:(UIButton *)btn{
    CGFloat secsAngle = M_PI / 30.f;
    [self showAnimationWithTime:secsAngle];
}

- (void)showAnimationWithTime:(CGFloat)angle {
    self.moveBtn.layer.transform = CATransform3DMakeRotation(angle, 0, 0, 1);
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        CGFloat secsAngle1;
        if (angle > 0) {
            secsAngle1 = -M_PI / 15.f;
        }else{
            secsAngle1 = M_PI / 15.f;
        }
        [self showAnimationWithTime:secsAngle1];
    });
}

- (void)viewDidLoad {
    [super viewDidLoad];
    [self.view addSubview:self.moveBtn];
    [self shakeAnimationForViewQ:self.moveBtn];
}

效果图

iOS实现一个轻微晃动的提示动画_第1张图片

第二种方案

起因:上面的方案用transform实现,有点僵硬,动画看上去不是很好,接下来用CAKeyframeAnimation来实现动画

- (UIButton *)moveBtn {
  if (!_moveBtn) {
   _moveBtn = [UIButton buttonWithType:UIButtonTypeCustom];
       _moveBtn.frame = CGRectMake(0, 0, 300, 150);
   _moveBtn.enabled = NO;
    [_moveBtn setBackgroundImage:[UIImage imageNamed:@"yangmi"] forState:UIControlStateDisabled];
    [_moveBtn setTitleColor:[UIColor whiteColor] forState:(UIControlStateNormal)];
    _moveBtn.titleLabel.font = [UIFont systemFontOfSize:30];
    [_moveBtn setTitle:@"我就是很美" forState:UIControlStateNormal];
    [_moveBtn setContentHorizontalAlignment:UIControlContentHorizontalAlignmentRight];
    _moveBtn.layer.anchorPoint = CGPointMake(0, 1);
    _moveBtn.layer.masksToBounds = YES;
    _moveBtn.layer.cornerRadius = 5.f;
      CGFloat width = [UIScreen mainScreen].bounds.size.width;
      CGFloat height = [UIScreen mainScreen].bounds.size.height;
     _moveBtn.layer.position = CGPointMake(width / 2.f - 150, height/2.f + 75);
    ///这个点设置很关键,可以设置摇动的锚点,这个设置动画在中间
  }
  return _moveBtn;
}

#pragma mark

- (void)addMoveAnimation:(UIView *)view{
 CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];
 animation.delegate = self;
 animation.keyPath = @"transform.rotation";
 animation.values = @[@(radian(0)), @(radian(-20)), @(radian(0)), @(radian(-20)),@(radian(0))];
 animation.duration = 0.7;
 // 动画的重复执行次数
 animation.repeatCount = 1;
 // 保持动画执行完毕后的状态
 animation.removedOnCompletion = NO;
 animation.fillMode = kCAFillModeForwards;
 [view.layer addAnimation:animation forKey:@"shake_animation"];
}

#pragma mark -- 动画代理
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    [self addMoveAnimation:self.moveBtn];
 });
}

#pragma mark - 移除抖动动画

- (void)removeAnimationShakeWithView:(UIView *)view{
 //结束动画
 [view.layer removeAnimationForKey:@"shake"];
}

- (void)viewDidLoad {
    [super viewDidLoad];
    [self.view addSubview:self.moveBtn];
    [self addMoveAnimation:self.moveBtn];
}

效果图

重要概念

  • anchorpoint
    相当于能旋转画的钉子
  • position
    anchorpoint在父layer上的坐标点
  • 指定position相当于指定了锚点
  • position点是相对suerLayer的,anchorPoint点是相对自己layer的,两者是相对不同的坐标空间的一个重合点。

思考题

  • 问:为什么在设置为anchorpoint点之后,需要重新设置frame或者position?
    答: 因为anchorpoint设置为(0,1)后,改变了之前默认的(0.5,0.5)
  • 改变anchorPoint后,能算出新的frame吗
  • 答: 可以,首先得算出最初的poisition,然后再计算
  • 问: 具体步骤
  • 答: 先算position,然后根据anchorpoint画图,再做计算,单方面改变anchorPoint一定会改变frame
  • 问:如果父视图变了,子视图没有变,子视图的frame和bounds会变吗
  • 答:不变
  • 问: 如果子视图的anchorPoint也变了呢
  • 答: 子视图根据父视图变化后的frame来计算

参考文章

  • 详解CALayer的anchorPoint和position
  • 彻底理解anchorPoint和position

你可能感兴趣的:(iOS,开发)