如何在 Objective C 中为浮动 UIView 设置动画
Posted
技术标签:
【中文标题】如何在 Objective C 中为浮动 UIView 设置动画【英文标题】:How to animate a floating UIView in ObjectiveC 【发布时间】:2014-09-10 11:02:36 【问题描述】:我正在尝试将UIView
动画化为浮动对象或气球:D
UIView
在屏幕中间,我希望它在第一个启动点周围随机浮动。不跨越屏幕或类似的东西,只是漂浮在它周围 5 个像素的区域内。
有什么建议吗? :D
我试过这个:
[UIView animateWithDuration:0.1
delay:0.0
options: UIViewAnimationOptionRepeat | UIViewAnimationOptionAutoreverse | UIViewAnimationOptionAllowUserInteraction
animations:^
myCircleUIView.transform = CGAffineTransformMakeTranslation([self randomFloatingGenerator], [self randomFloatingGenerator]);
completion:NULL];
randomFloatingGenerator
生成一个介于 -5 和 5 之间的数字。问题是,它只执行一次,并且不断重复使用相同的随机值。
EDIT1: 现在我已经尝试过了
-(void)animationLoop
[UIView animateWithDuration:1.0
animations: ^ myCircleUIView.transform = CGAffineTransformMakeTranslation([self randomFloatingGenerator], [self randomFloatingGenerator]);
completion:
^(BOOL finished)
[UIView animateWithDuration:1.0
animations:^ myCircleUIView.transform = CGAffineTransformMakeTranslation(0,0);
completion:
^(BOOL finished) [self animationLoop];];
];
但它仍然无法正常工作,动画......糟糕......我想我正在犯一些愚蠢的错误,我需要第二双眼睛来帮助我。
EDIT2: 修好了。
-(void)animationLoop
CGPoint oldPoint = CGPointMake(myCircleUIView.frame.origin.x, myCircleUIView.frame.origin.y);
[UIView animateWithDuration:1.0
animations: ^ myCircleUIView.frame = CGRectMake(myCircleUIView.frame.origin.x + [self randomFloatingGenerator], myCircleUIView.frame.origin.y + [self randomFloatingGenerator], myCircleUIView.frame.size.width, myCircleUIView.frame.size.height);
completion:
^(BOOL finished)
[UIView animateWithDuration:1.0
animations:^ myCircleUIView.frame = CGRectMake(oldPoint.x, oldPoint.y, myCircleUIView.frame.size.width, myCircleUIView.frame.size.height);
completion:
^(BOOL finished) [self animationLoop];];
];
感谢任何人的帮助。
编辑 3:
有人发布了增强的代码。
【问题讨论】:
使用 NSTimer 并在其中调用上面的代码... 【参考方案1】:@Shamy 的解决方案,经过改进、修复和 CPU 安全。
-(void)animateFloatView:(UIView*)view
// Abort the recursive loop
if (!view)
return;
CGPoint oldPoint = CGPointMake(view.frame.origin.x, view.frame.origin.y);
[UIView animateWithDuration:0.6
animations: ^ view.frame = CGRectMake(view.frame.origin.x, view.frame.origin.y - 15, view.frame.size.width, view.frame.size.height);
completion:
^(BOOL finished)
if (finished)
[UIView animateWithDuration:0.6
animations:^ view.frame = CGRectMake(oldPoint.x, oldPoint.y, view.frame.size.width, view.frame.size.height);
completion:
^(BOOL finished2)
if(finished2)
[self animateFloatView:view];
];
];
当你想停止动画时(离开 View Controller 时需要),使用 nil 调用函数,如下所示:
[self recursiveFloatingAnimation:nil];
不停止动画会导致递归循环无限运行,压垮CPU栈。
【讨论】:
【参考方案2】:斯威夫特 4
UIView.animate(withDuration: 0.6, delay: 0.1, options: [.autoreverse, .repeat], animations:
self.frame = CGRect(x: self.frame.origin.x, y: self.frame.origin.y - 15, width: self.frame.size.width, height: self.frame.size.height)
,completion: nil )
【讨论】:
如果您在 X 和 Y 中使用两个介于 -15 和 15 之间的随机变量来制作 Delta,它将非常适合我尝试做的事情。【参考方案3】:它将以这种方式运行。当您重复动画而不是功能时。因此,每次您在第一次调用时设置时,它都会重复相同的动画。这样做:
@interface AAViewController ()
@property (nonatomic, strong) UIView * floatingView;
@end
@implementation AAViewController
- (void)viewDidLoad
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
_floatingView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 50, 50)];
[_floatingView setBackgroundColor:[UIColor redColor]];
[self.view addSubview:_floatingView];
[self circleUIViewFloating];
- (void)didReceiveMemoryWarning
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
#pragma mark - Circle UIView floating
- (void) circleUIViewFloating
[UIView animateWithDuration:2.0
animations:^
_floatingView.transform = CGAffineTransformMakeTranslation([self randomFloatingGenerator : 270], [self randomFloatingGenerator:480]);
completion:^(BOOL finished)
[self circleUIViewFloating];
];
- (int) randomFloatingGenerator : (int) max
return arc4random() % max;
这里是complete running project。
【讨论】:
这样做的问题是它翻译了对象,所以当它完成时,它的位置不是它开始的位置,而是它翻译到的位置。我不想翻译对象,我只想为它设置动画。如果我使用重新动画,它会继续跳跃,如果我不使用它,它会继续自行漂浮,而不是像我试图做的那样围绕其原始坐标。 我不确定 UIkit 是否适合您的任务。如果您想编写游戏代码,请查看 spriteKit、Cocos2d 或类似产品! 否则看时序曲线。使用easeInEaseOut 避免突然移动。 问题似乎是,它只是从我希望它动画的位置开始动画,而不是从,但不明白为什么......【参考方案4】:您可以使用CABasicAnimation
实现悬停或浮动效果。
您必须包含QuartzCore
框架才能使用它。
Swift 4.2:
import QuartzCore
let hover = CABasicAnimation(keyPath: "position")
hover.isAdditive = true
hover.fromValue = NSValue(cgPoint: CGPoint.zero)
hover.toValue = NSValue(cgPoint: CGPoint(x: 0.0, y: -5.0))
hover.autoreverses = true
hover.duration = 0.8
hover.repeatCount = Float.infinity
myCustomView.layer.add(hover, forKey: "hoverAnimation")
确保在不再显示视图时移除动画。
override func viewWillDisappear(_ animated: Bool)
// Stop animating when view is going to disappear
myCustomView.layer.removeAllAnimations()
【讨论】:
【参考方案5】:-(void)animationLoop
CGPoint oldPoint = CGPointMake(myCircleUIView.frame.origin.x, myCircleUIView.frame.origin.y);
[UIView animateWithDuration:1.0
animations: ^ myCircleUIView.frame = CGRectMake(myCircleUIView.frame.origin.x + [self randomFloatingGenerator], myCircleUIView.frame.origin.y + [self randomFloatingGenerator], myCircleUIView.frame.size.width, myCircleUIView.frame.size.height);
completion:
^(BOOL finished)
[UIView animateWithDuration:1.0
animations:^ myCircleUIView.frame = CGRectMake(oldPoint.x, oldPoint.y, myCircleUIView.frame.size.width, myCircleUIView.frame.size.height);
completion:
^(BOOL finished) [self animationLoop];];
];
【讨论】:
这个递归解决方案阻塞了主线程。我强烈反对使用此解决方案,除非您在离开 VC 之前将其停止。此外,您需要在再次调用之前检查当前动画是否准备就绪,否则您将进入无限循环。 是的,我在想这个,那么你将如何停止和启动动画?使用 UIViewPropertyAnimator?【参考方案6】:Swift 2.1 版本在这里:
func animateFloatView(view: UIView?)
// Abort the recursive loop
guard let view = view else return
let oldPoint: CGPoint = CGPointMake(view.frame.origin.x, view.frame.origin.y)
UIView.animateWithDuration(0.6, animations: () -> Void in
view.frame = CGRectMake(view.frame.origin.x, view.frame.origin.y - 15, view.frame.size.width, view.frame.size.height)
, completion: (finished: Bool) -> Void in
if finished
UIView.animateWithDuration(0.6, animations: () -> Void in
view.frame = CGRectMake(oldPoint.x, oldPoint.y, view.frame.size.width, view.frame.size.height)
, completion: (finished2: Bool) -> Void in
if finished2
self.animateFloatView(view)
)
)
【讨论】:
【参考方案7】:Swift 3.1:
func animateFloatView(_ view: UIView?)
guard let view = view else return
let oldYCoordinate = view.center.y
UIView.animate(withDuration: 0.6, animations:
view.center.y -= 15
, completion: _ in
UIView.animate(withDuration: 0.6, animations:
view.center.y = oldYCoordinate
, completion: _ in
self.animateFloatView(view)
)
)
【讨论】:
你如何打破这个动画,比如阻止它浮动?以上是关于如何在 Objective C 中为浮动 UIView 设置动画的主要内容,如果未能解决你的问题,请参考以下文章
在 Objective C 中为 UISlider 创建没有情节提要的 Outlet 和 Action
Objective C 使用“完成”按钮实现 UIPickerView
在 Swift 中为 Objective C 函数提供 NSMutableArray 类型
在 Swift 中为变量属性赋值的 Objective C 语法