Dawid
Dawid

Reputation: 715

iOS: UIView animation ignores delay

I have a problem with very simple UIView animation:

- (void)showView:(CGFloat)delay
{
    [UIView animateWithDuration:1.0 delay:delay options:0 animations:^{

        // self.alpha = 0.5; // Works OK with delay
        self.frame = CGRectMake(100, 100, 100, 30); // delay is ignored
    } completion:nil];
}

delay could be set to 1000 and still view is animated immediately. But somehow it works fine with alpha (without setting frame).

Upvotes: 3

Views: 2982

Answers (4)

Instead of

UIView.animate(
    withDuration: 0.2, 
    delay: 1, 
    options: .curveLinear, 
    animations: {
        self.someButton.isHidden = false
    })
}

Do just:

DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
    UIView.animate(withDuration: 0.2, 
        delay: 0, 
        options: .curveLinear, 
        animations: {
        self.someButton.isHidden = false
    })
}

Upvotes: 0

CartoonChess
CartoonChess

Reputation: 683

This answer uses Swift, specifically version 4.2, and is tested in Xcode 10 playgrounds. This is actually my first try animating, and I'm very green with iOS in general, but this is how I solved the problem of delay not being observed.

import UIKit
import PlaygroundSupport

// Set up the main view
let liveViewFrame = CGRect(x: 0, y: 0, width: 500, height: 500)
let liveView = UIView(frame: liveViewFrame)
liveView.backgroundColor = .white

// Show the live view using the main view
PlaygroundPage.current.liveView = liveView

// Set up a smaller view for animation
let smallFrame = CGRect(x: 0, y: 0, width: 100, height: 100)
let square = UIView(frame: smallFrame)
square.backgroundColor = .purple

// Add the square to the live view ...
liveView.addSubview(square)

// ... and create an alias for it to allow delays to work.
// Calling square directly won't allow delays,
// giving it or the liveView subview an alias directly fails,
// and unwrapping (? or !) appears to fail as well.
// So, just call like `alias?.backgroundColor` ...
let squareView = square.superview?.subviews[0]

UIView.animate(withDuration: 1.5, delay: 1.0, options: [.repeat], animations: {
    //square.backgroundColor = .orange // this doesn't respect the delay arg
    squareView?.backgroundColor = .orange
    squareView?.frame = CGRect(x: 150, y: 150, width: 200, height: 200)
    squareView?.transform = CGAffineTransform(rotationAngle: CGFloat(Double.pi))
}, completion: nil)

This may not answer the question directly, but I believe similar logic could be applied for a workaround in related cases.

Upvotes: 0

[self performSelector:@selector(animateMe) withObject:nil afterDelay:1000];

If self is viewController then you should use self.view.frame instead of self.frame. If self is view then you should use self.frame.

- (void)animateMe {

    [UIView animateWithDuration:1.0f animations:^{

        // self.alpha = 0.5; // Works OK with delay
        self.frame = CGRectMake(100, 100, 100, 30); // delay is ignored
    } completion:^(BOOL finished) {

        NSLog(@"Done");
    }];            
}

Upvotes: 0

jrturton
jrturton

Reputation: 119292

The frame is probably being set for you, perhaps by layout being performed by the superview, or this view itself. You haven't said how you're adding this view or what it does so its difficult to give specific advice, but in general terms:

  • Don't let a view be in control of its own frame - this is the superview's or view controllers responsibility. A view can have a preferred size, but the rest is outside.
  • it's usually better to animate bounds and / or centre instead of frame, particularly if you're ignoring the first point.
  • For presentation type animations, it may be better to animate the transform instead - either translation or scale, depending what your animation is.

Upvotes: 1

Related Questions