¿Hacer un fundido simple en la animación en Swift?

82

Estoy tratando de hacer una animación simple en Swift. Es un desvanecimiento.

Lo intenté:

self.myFirstLabel.alpha = 0
self.myFirstButton.alpha = 0
self.mySecondButton.alpha = 0

Luego tengo:

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

Y entonces:

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

Tengo todo esto en mi función viewDidLoad.

¿Cómo hago que esto funcione?

Benr783
fuente
Todos los elementos entran al mismo tiempo. Justo cuando se carga la vista.
Benr783
¿Está seguro de que las vistas están conectadas correctamente en el xib?
drewag
No estoy usando IB. Todo es programáticamente.
Benr783
Bueno, por el código que proporcionaste, todo debería funcionar (y esto no tiene nada que ver con Swift). Debe haber un error en otro lugar.
drewag
Considere mover el método de puntos de vista de línea de llamada a viewWillAppearo viewDidAppearcomo usted no sabe lo que pasa entre la vista que se carga y se muestra.
A-Live

Respuestas:

121

El problema es que está intentando iniciar la animación demasiado pronto en el ciclo de vida del controlador de vista. En viewDidLoad, la vista se acaba de crear y aún no se ha agregado a la jerarquía de vistas, por lo que intentar animar una de ellas subviewsen este punto produce malos resultados.

Lo que realmente debería hacer es continuar configurando el alfa de la vista en viewDidLoad(o donde crea sus vistas), y luego esperar viewDidAppeara que se llame al método :. En este punto, puede iniciar sus animaciones sin ningún problema.

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
    }
}
Mick MacCallum
fuente
¿Es viewWillAppearmás apropiado para esto?
ton
1
Recuerde, si solo tiene 1 declaración, debe agregar una devoluciónUIView.animateWithDuration(1.5, animations: { self.myFirstLabel.alpha = 1.0 return })
oshevans
@oshevans ¿Se refiere a la necesidad de agregar una declaración de devolución al final del cierre? Esto se ha solucionado principalmente en las versiones más recientes de Xcode.
Mick MacCallum
Tengo un error muy similar. Por favor, vea si puede ayudarme: stackoverflow.com/questions/29910669/…
Teo Inke
@ 0x7fffffff '... en su mayoría arreglado ...' - eso no suena prometedor; / Aún por actualizar desde 6.2
oshevans
46

La respuesta de 0x7ffffff está bien y definitivamente es exhaustiva.

Como ventaja, te sugiero que hagas una extensión UIView, de esta manera:

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

De esta manera, puede hacer esto en cualquier lugar de su código:

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

¡La reutilización del código es importante!

Luca Davanzo
fuente
7

La única solución rápida

Similar a la respuesta de Luca , utilizo una UIViewextensión. En comparación con su solución, utilizo DispatchQueue.main.asyncpara asegurarme de que las animaciones se realicen en el hilo principal, alphaparámetro para desvanecer a un valor específico y durationparámetros opcionales para código más limpio.

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

Cómo usarlo:

// 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)
budiDino
fuente
5

Si desea una animación de fundido repetible, puede hacerlo usando CABasicAnimationcomo se muestra a continuación:

Primero cree una práctica extensión 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)
    }
}

Cómo usarlo:

    // 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)
cs4alhaider
fuente
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")
    }

}
LLIAJLbHOu
fuente
0

Rápido 5

Otras respuestas son correctas, pero en mi caso tengo que manejar otras propiedades también ( alpha, animate, completion). Debido a esto, modifiqué un poco para exponer estos parámetros de la siguiente manera:

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