简体   繁体   中英

How to animate a floating UIView in ObjectiveC

I am trying to have a UIView animate as a floating object, or as a Balloon :D The UIView is in the middle of the screen, and I want it to keep floating randomly around its first initiated spot. Not across the screen or anything like that, just floating in the area of 5 pixels around it.

Any suggestions? :D

I tried this:

[UIView animateWithDuration:0.1
                      delay:0.0
                    options: UIViewAnimationOptionRepeat | UIViewAnimationOptionAutoreverse | UIViewAnimationOptionAllowUserInteraction
                 animations:^{
                     myCircleUIView.transform = CGAffineTransformMakeTranslation([self randomFloatingGenerator], [self randomFloatingGenerator]);
                 }
                 completion:NULL];

randomFloatingGenerator generates a number between -5 and 5. Problem is, it only executes once, and keeps repeating with the same random values.

EDIT1: Now I have tried This

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

But it is still not working, the animation is.... Cracky... I think I am doing some stupid mistake that I need a second set of eyes to help me with.

EDIT2: Fixed it.

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

Thanks for the help anyone.

EDIT 3:

Someone posted an enhanced code.

@Shamy's solution, improved, fixed and CPU safe.

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

         }
     }];
}

Whenever you want to stop the animation (necessary when leaving the View Controller), call the function using nil , as this:

    [self recursiveFloatingAnimation:nil];

Not stoping the animation will cause the recursive loop to run infinitely and overwhelm the CPU stack.

It will behave in that way. As you are repeating the animation not the function. So it will repeat same animation every time you set at first call. Do it like:

@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;
}

Here is complete running project for this.

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

Swift 2.1 Version Here:

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

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

You can achieve the hovering or floating effect using CABasicAnimation .

You will have to include the QuartzCore framework to use this.

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")

Make sure to remove the animation when the view is no longer being shown.

    override func viewWillDisappear(_ animated: Bool) {
        // Stop animating when view is going to disappear
        myCustomView.layer.removeAllAnimations()
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM