如何在 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 语法

在 Objective C 中为自定义类实现委托时的引用计数

如何在 CollectionViewController 中为 gif 设置动画.. (Objective-C)