背景
我们需要用到一个上下轻微晃动的动画效果,提示有什么东西,吸引用户的注意。
方案一
- (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];
}
效果图
第二种方案
起因:上面的方案用transform实现,有点僵硬,动画看上去不是很好,接下来用CAKeyframeAnimation来实现动画
#define radian(angle) ((angle) / 180.0 * M_PI)
- (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来计算
2023年5月14学习
1、改变了anchorpoint不会改变position值
2、改变anchorpoint会让视图移动,要想让视图回到原先位置,就需要改动position
3、position.x = originframe.origin.x + anchorPoint.x * bounds.size.width;
position.y = originframe.origin.y + anchorPoint.y * bounds.size.height;
怎么计算posision,原先的x和y,加上后面的值
4、我们要解释的anchorPoint就相当于白纸上的图钉,它主要的作用就是用来作为变换的支点,旋转就是一种变换,类似的还有平移、缩放。(摘自下面的文章)