在Swift中制作一个简单的淡入动画?

96
我正在尝试在Swift中制作一个简单的动画,它是一个淡入效果。 我尝试了:
self.myFirstLabel.alpha = 0
self.myFirstButton.alpha = 0
self.mySecondButton.alpha = 0

然后,我有:

self.view.addSubview(myFirstLabel)
self.view.addSubview(myFirstButton)
self.view.addSubview(mySecondButton)

然后:

UIView.animateWithDuration(1.5, animations: {
 self.myFirstLabel.alpha = 1.0
 self.myFirstButton.alpha = 1.0
 self.mySecondButton.alpha = 1.0
})

我在viewDidLoad函数中拥有所有这些内容。

我该如何让它工作?


所有元素同时出现。就在视图加载的时候。 - Benr783
你确定xib文件中的视图已经正确连接了吗? - drewag
我没有使用IB。一切都是以编程方式实现的。 - Benr783
从您提供的代码来看,一切都应该可以正常工作(这与Swift无关)。肯定是其他地方出现了错误。 - drewag
考虑把视图的外观方法调用移到 viewWillAppearviewDidAppear 中,因为你不知道视图加载和显示之间会发生什么。 - A-Live
6个回答

137
问题在于您尝试在视图控制器生命周期的太早阶段启动动画。在 viewDidLoad 中,视图刚刚被创建,并且尚未添加到视图层次结构中,因此在此时尝试动画化其子视图之一会产生不良结果。
您真正应该做的是在 viewDidLoad(或创建视图的位置)继续设置视图的 alpha 值,然后等待调用 viewDidAppear: 方法。在此时,您可以开始您的动画而没有任何问题。
override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)

    UIView.animate(withDuration: 1.5) {
        self.myFirstLabel.alpha = 1.0
        self.myFirstButton.alpha = 1.0
        self.mySecondButton.alpha = 1.0
    }
}

1
记住,如果您只有1条语句,您需要添加一个返回值 UIView.animateWithDuration(1.5, animations: { self.myFirstLabel.alpha = 1.0 return }) - oshevans
@oshevans 您是指需要在闭包末尾添加返回语句吗?这在最新的Xcode版本中已经大部分得到修复。 - Mick MacCallum
我有一个非常类似的错误。请看看能否帮助我:http://stackoverflow.com/questions/29910669/swift-when-or-how-to-implement-animations-on-view-start - Teo Inke
@0x7fffffff:“……大部分都已经修复了……”——这听起来不太乐观;/ 但我还没有从6.2更新。 - oshevans
@oshevans 我建议你使用6.3版本(发布说明)。这个问题在这个版本中已经被官方修复。我之前说“大部分”只是为了谨慎,但自从升级后我就没有再遇到这个问题了。 - Mick MacCallum
显示剩余2条评论

54

0x7ffffff的答案很好,而且绝对详尽。

此外,我建议您制作一个UIView扩展,方式如下:

public extension UIView {

  /**
  Fade in a view with a duration

  - parameter duration: custom animation duration
  */
  func fadeIn(duration duration: NSTimeInterval = 1.0) {
    UIView.animateWithDuration(duration, animations: {
        self.alpha = 1.0
    })
  }

  /**
  Fade out a view with a duration

  - parameter duration: custom animation duration
  */
  func fadeOut(duration duration: NSTimeInterval = 1.0) {
    UIView.animateWithDuration(duration, animations: {
        self.alpha = 0.0
    })
  }

}

Swift-3

/// Fade in a view with a duration
/// 
/// Parameter duration: custom animation duration
func fadeIn(withDuration duration: TimeInterval = 1.0) {
    UIView.animate(withDuration: duration, animations: {
        self.alpha = 1.0
    })
}

/// Fade out a view with a duration
///
/// - Parameter duration: custom animation duration
func fadeOut(withDuration duration: TimeInterval = 1.0) {
    UIView.animate(withDuration: duration, animations: {
        self.alpha = 0.0
    })
}

Swift-5

public extension UIView {

/**
 Fade in a view with a duration
 
 - parameter duration: custom animation duration
 */
 func fadeIn(duration: TimeInterval = 1.0) {
     UIView.animate(withDuration: duration, animations: {
        self.alpha = 1.0
     })
 }

/**
 Fade out a view with a duration
 
 - parameter duration: custom animation duration
 */
func fadeOut(duration: TimeInterval = 1.0) {
    UIView.animate(withDuration: duration, animations: {
        self.alpha = 0.0
    })
  }

}

这样,您可以在代码的任何位置执行此操作:

let newImage = UIImage(named: "")
newImage.alpha = 0 // or newImage.fadeOut(duration: 0.0)
self.view.addSubview(newImage)
... 
newImage.fadeIn()

代码重用很重要!


8

仅限Swift解决方案

类似于Luca的回答,我使用一个UIView扩展。与他的解决方案相比,我使用DispatchQueue.main.async确保动画在主线程上完成,使用alpha参数来淡化至特定值,并使用可选的duration参数使代码更加简洁。

extension UIView {
  func fadeTo(_ alpha: CGFloat, duration: TimeInterval = 0.3) {
    DispatchQueue.main.async {
      UIView.animate(withDuration: duration) {
        self.alpha = alpha
      }
    }
  }

  func fadeIn(_ duration: TimeInterval = 0.3) {
    fadeTo(1.0, duration: duration)
  }

  func fadeOut(_ duration: TimeInterval = 0.3) {
    fadeTo(0.0, duration: duration)
  }
}

如何使用:

// fadeIn() - always animates to alpha = 1.0
yourView.fadeIn()     // uses default duration of 0.3
yourView.fadeIn(1.0)  // uses custom duration (1.0 in this example)

// fadeOut() - always animates to alpha = 0.0
yourView.fadeOut()    // uses default duration of 0.3
yourView.fadeOut(1.0) // uses custom duration (1.0 in this example)

// fadeTo() - used if you want a custom alpha value
yourView.fadeTo(0.5)  // uses default duration of 0.3
yourView.fadeTo(0.5, duration: 1.0)

8

如果你想要可重复的淡入淡出动画,你可以使用下面的CABasicAnimation

首先创建一个方便的UIView扩展:

extension UIView {

    enum AnimationKeyPath: String {
        case opacity = "opacity"
    }

    func flash(animation: AnimationKeyPath ,withDuration duration: TimeInterval = 0.5, repeatCount: Float = 5){
        let flash = CABasicAnimation(keyPath: animation.rawValue)
        flash.duration = duration
        flash.fromValue = 1 // alpha
        flash.toValue = 0 // alpha
        flash.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
        flash.autoreverses = true
        flash.repeatCount = repeatCount

        layer.add(flash, forKey: nil)
    }
}

如何使用它:

    // You can use it with all kind of UIViews e.g. UIButton, UILabel, UIImage, UIImageView, ...
    imageView.flash(animation: .opacity, withDuration: 1, repeatCount: 5)
    titleLabel.flash(animation: .opacity, withDuration: 1, repeatCount: 5)

1

Swift 5

其他答案是正确的,但在我的情况下,我还需要处理其他属性(alphaanimatecompletion)。因此,我进行了一些修改,以以下方式公开这些参数:

extension UIView {
    /// Helper function to update view's alpha with animation
    /// - Parameter alpha: View's alpha
    /// - Parameter animate: Indicate alpha changing with animation or not
    /// - Parameter duration: Indicate time for animation
    /// - Parameter completion: Completion block after alpha changing is finished
    func set(alpha: CGFloat, animate: Bool, duration: TimeInterval = 0.3, completion: ((Bool) -> Void)? = nil) {
        let animation = { (view: UIView) in
            view.alpha = alpha
        }
    
        if animate {
            UIView.animate(withDuration: duration, animations: {
                animation(self)
            }, completion: { finished in
                completion?(finished)
            })
        } else {
            layer.removeAllAnimations()
            animation(self)
            completion?(true)
        }
    }
}

0
import UIKit

/*
 Here is simple subclass for CAAnimation which create a fadeIn animation
 */

class FadeInAdnimation: CABasicAnimation {
    override init() {
        super.init()
        keyPath = "opacity"
        duration = 2.0
        fromValue = 0
        toValue = 1
        fillMode = CAMediaTimingFillMode.forwards
        isRemovedOnCompletion = false
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
}

/*
 Example of usage
 */

class ViewController: UIViewController {

    weak var label: UILabel!

    override func loadView() {
        let view = UIView()
        view.backgroundColor = .white

        let label = UILabel()
        label.alpha = 0
        label.frame = CGRect(x: 150, y: 200, width: 200, height: 20)
        label.text = "Hello World!"
        label.textColor = .black
        view.addSubview(label)
        self.label = label

        let button = UIButton(type: .custom)
        button.frame = CGRect(x: 0, y: 250, width: 300, height: 100)
        button.setTitle("Press to Start FadeIn", for: UIControl.State())
        button.backgroundColor = .red
        button.addTarget(self, action: #selector(startFadeIn), for: .touchUpInside)
        view.addSubview(button)

        self.view = view
    }

    /*
     Animation in action
     */
    @objc private func startFadeIn() {
        label.layer.add(FadeInAdnimation(), forKey: "fadeIn")
    }

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接