美文网首页
登录页面动画示例

登录页面动画示例

作者: _浅墨_ | 来源:发表于2022-03-17 21:11 被阅读0次
    login 动画

    重读此 demo,温故而知新。

    这里 delay(seconds: Double, completion: @escaping ()-> Void) 和 textFieldDidEndEditing(_ textField: UITextField) 方法不错,可以抽出来经常使用。

    附:源码如下:

    
    import UIKit
    
    // A delay function
    func delay(seconds: Double, completion: @escaping ()-> Void) {
      DispatchQueue.main.asyncAfter(deadline: .now() + seconds, execute: completion)
    }
    
    func tintBackgroundColor(layer: CALayer, toColor: UIColor) {
      let tint = CABasicAnimation(keyPath: "backgroundColor")
      tint.fromValue = layer.backgroundColor
      tint.toValue = toColor.cgColor
      tint.duration = 0.5
      layer.add(tint, forKey: nil)
      layer.backgroundColor = toColor.cgColor
    }
    
    func roundCorners(layer: CALayer, toRadius: CGFloat) {
    //  let round = CABasicAnimation(keyPath: "cornerRadius")
    //  round.fromValue = layer.cornerRadius
    //  round.toValue = toRadius
    //  round.duration = 0.5
    //  layer.add(round, forKey: nil)
    //  layer.cornerRadius = toRadius
        
        let round = CASpringAnimation(keyPath: "cornerRadius")
        round.damping = 5.0
        round.fromValue = layer.cornerRadius
        round.toValue = toRadius
        round.duration = round.settlingDuration
        layer.add(round, forKey: nil)
        layer.cornerRadius = toRadius
    }
    
    class ViewController: UIViewController {
    
      // MARK: IB outlets
    
      @IBOutlet var loginButton: UIButton!
      @IBOutlet var heading: UILabel!
      @IBOutlet var username: UITextField!
      @IBOutlet var password: UITextField!
    
      @IBOutlet var cloud1: UIImageView!
      @IBOutlet var cloud2: UIImageView!
      @IBOutlet var cloud3: UIImageView!
      @IBOutlet var cloud4: UIImageView!
    
      // MARK: further UI
    
      let spinner = UIActivityIndicatorView(activityIndicatorStyle: .whiteLarge)
      let status = UIImageView(image: UIImage(named: "banner"))
      let label = UILabel()
      let messages = ["Connecting ...", "Authorizing ...", "Sending credentials ...", "Failed"]
    
      var statusPosition = CGPoint.zero
    
      let info = UILabel()
    
      // MARK: view controller methods
    
      override func viewDidLoad() {
        super.viewDidLoad()
    
        //set up the UI
        loginButton.layer.cornerRadius = 8.0
        loginButton.layer.masksToBounds = true
    
        spinner.frame = CGRect(x: -20.0, y: 6.0, width: 20.0, height: 20.0)
        spinner.startAnimating()
        spinner.alpha = 0.0
        loginButton.addSubview(spinner)
    
        status.isHidden = true
        status.center = loginButton.center
        view.addSubview(status)
    
        label.frame = CGRect(x: 0.0, y: 0.0, width: status.frame.size.width, height: status.frame.size.height)
        label.font = UIFont(name: "HelveticaNeue", size: 18.0)
        label.textColor = UIColor(red: 0.89, green: 0.38, blue: 0.0, alpha: 1.0)
        label.textAlignment = .center
        status.addSubview(label)
    
        statusPosition = status.center
    
        info.frame = CGRect(x: 0.0, y: loginButton.center.y + 60.0,  width: view.frame.size.width, height: 30)
        info.backgroundColor = UIColor.clear
        info.font = UIFont(name: "HelveticaNeue", size: 12.0)
        info.textAlignment = .center
        info.textColor = UIColor.white
        info.text = "Tap on a field and enter username and password"
        view.insertSubview(info, belowSubview: loginButton)
      }
    
      override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
    
        let formGroup = CAAnimationGroup()
        formGroup.duration = 0.5
        formGroup.fillMode = kCAFillModeBackwards
    
        let flyRight = CABasicAnimation(keyPath: "position.x")
        flyRight.fromValue = -view.bounds.size.width/2
        flyRight.toValue = view.bounds.size.width/2
    
        let fadeFieldIn = CABasicAnimation(keyPath: "opacity")
        fadeFieldIn.fromValue = 0.25
        fadeFieldIn.toValue = 1.0
    
        formGroup.animations = [flyRight, fadeFieldIn]
        heading.layer.add(formGroup, forKey: nil)
    
        formGroup.delegate = self
        formGroup.setValue("form", forKey: "name")
        formGroup.setValue(username.layer, forKey: "layer")
    
        formGroup.beginTime = CACurrentMediaTime() + 0.3
        username.layer.add(formGroup, forKey: nil)
    
        formGroup.setValue(password.layer, forKey: "layer")
        formGroup.beginTime = CACurrentMediaTime() + 0.4
        password.layer.add(formGroup, forKey: nil)
      }
    
      override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
    
        let fadeIn = CABasicAnimation(keyPath: "opacity")
        fadeIn.fromValue = 0.0
        fadeIn.toValue = 1.0
        fadeIn.duration = 0.5
        fadeIn.fillMode = kCAFillModeBackwards
        fadeIn.beginTime = CACurrentMediaTime() + 0.5
        cloud1.layer.add(fadeIn, forKey: nil)
    
        fadeIn.beginTime = CACurrentMediaTime() + 0.7
        cloud2.layer.add(fadeIn, forKey: nil)
    
        fadeIn.beginTime = CACurrentMediaTime() + 0.9
        cloud3.layer.add(fadeIn, forKey: nil)
    
        fadeIn.beginTime = CACurrentMediaTime() + 1.1
        cloud4.layer.add(fadeIn, forKey: nil)
    
        let groupAnimation = CAAnimationGroup()
        groupAnimation.beginTime = CACurrentMediaTime() + 0.5
        groupAnimation.duration = 0.5
        groupAnimation.fillMode = kCAFillModeBackwards
    
        let scaleDown = CABasicAnimation(keyPath: "transform.scale")
        scaleDown.fromValue = 3.5
        scaleDown.toValue = 1.0
    
        let rotate = CABasicAnimation(keyPath: "transform.rotation")
        rotate.fromValue = .pi / 4.0
        rotate.toValue = 0.0
    
        let fade = CABasicAnimation(keyPath: "opacity")
        fade.fromValue = 0.0
        fade.toValue = 1.0
    
        groupAnimation.animations = [scaleDown, rotate, fade]
        groupAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn)
        loginButton.layer.add(groupAnimation, forKey: nil)
    
        animateCloud(layer: cloud1.layer)
        animateCloud(layer: cloud2.layer)
        animateCloud(layer: cloud3.layer)
        animateCloud(layer: cloud4.layer)
    
        let flyLeft = CABasicAnimation(keyPath: "position.x")
        flyLeft.fromValue = info.layer.position.x + view.frame.size.width
        flyLeft.toValue = info.layer.position.x
        flyLeft.duration = 5.0
        info.layer.add(flyLeft, forKey: "infoappear")
    
        let fadeLabelIn = CABasicAnimation(keyPath: "opacity")
        fadeLabelIn.fromValue = 0.2
        fadeLabelIn.toValue = 1.0
        fadeLabelIn.duration = 4.5
        info.layer.add(fadeLabelIn, forKey: "fadein")
    
        username.delegate = self
        password.delegate = self
      }
    
      func showMessage(index: Int) {
        label.text = messages[index]
    
        UIView.transition(with: status, duration: 0.33,
          options: [.curveEaseOut, .transitionFlipFromBottom],
          animations: {
            self.status.isHidden = false
          },
          completion: {_ in
            //transition completion
            delay(seconds: 2.0) {
              if index < self.messages.count-1 {
                self.removeMessage(index: index)
              } else {
                //reset form
                self.resetForm()
              }
            }
          }
        )
      }
    
      func removeMessage(index: Int) {
    
        UIView.animate(withDuration: 0.33, delay: 0.0,
          animations: {
            self.status.center.x += self.view.frame.size.width
          },
          completion: {_ in
            self.status.isHidden = true
            self.status.center = self.statusPosition
    
            self.showMessage(index: index+1)
          }
        )
      }
    
      func resetForm() {
        UIView.transition(with: status, duration: 0.2, options: .transitionFlipFromTop,
          animations: {
            self.status.isHidden = true
            self.status.center = self.statusPosition
          },
          completion: { _ in
            let tintColor = UIColor(red: 0.63, green: 0.84, blue: 0.35, alpha: 1.0)
            tintBackgroundColor(layer: self.loginButton.layer, toColor: tintColor)
            roundCorners(layer: self.loginButton.layer, toRadius: 10.0)
        })
    
        UIView.animate(withDuration: 0.2, delay: 0.0,
          animations: {
            self.spinner.center = CGPoint(x: -20.0, y: 16.0)
            self.spinner.alpha = 0.0
            self.loginButton.bounds.size.width -= 80.0
            self.loginButton.center.y -= 60.0
          },
          completion: nil
        )
        
        
        let wobble = CAKeyframeAnimation(keyPath: "transform.rotation")
        wobble.duration = 0.25
        wobble.repeatCount = 4
        wobble.values = [0.0, -.pi/4.0, 0.0, .pi/4.0, 0.0]
        wobble.keyTimes = [0.0, 0.25, 0.5, 0.75, 1.0]
        heading.layer.add(wobble, forKey: nil)
      }
    
      // MARK: further methods
    
      @IBAction func login() {
        view.endEditing(true)
    
        UIView.animate(withDuration: 1.5, delay: 0.0, usingSpringWithDamping: 0.2,
          initialSpringVelocity: 0.0,
          animations: {
            self.loginButton.bounds.size.width += 80.0
          },
          completion: {_ in
            self.showMessage(index: 0)
          }
        )
    
        UIView.animate(withDuration: 0.33, delay: 0.0, usingSpringWithDamping: 0.7,
          initialSpringVelocity: 0.0,
          animations: {
            self.loginButton.center.y += 60.0
            self.spinner.center = CGPoint(x: 40.0, y: self.loginButton.frame.size.height/2)
            self.spinner.alpha = 1.0
          },
          completion: nil
        )
    
        let tintColor = UIColor(red: 0.85, green: 0.83, blue: 0.45, alpha: 1.0)
        tintBackgroundColor(layer: loginButton.layer, toColor: tintColor)
        roundCorners(layer: loginButton.layer, toRadius: 25.0)
        
        let balloon = CALayer()
        balloon.contents = UIImage(named: "balloon")!.cgImage
        balloon.frame = CGRect(x: -50.0, y: 0.0, width:50.0, height: 65.0)
        view.layer.insertSublayer(balloon, below: username.layer)
        
        let flight = CAKeyframeAnimation(keyPath: "position")
        flight.duration = 12.0
        flight.values = [
            CGPoint(x: -50.0, y: 0.0),
            CGPoint(x: view.frame.width + 50.0, y: 160.0),
            CGPoint(x:-50.0,y:loginButton.center.y)
        ].map{NSValue(cgPoint: $0)}
        flight.keyTimes = [0.0, 0.5, 1.0]
        
        balloon.add(flight, forKey: nil)
        balloon.position = CGPoint(x: -50.0, y: loginButton.center.y)
        
      }
    
      func animateCloud(layer: CALayer) {
    
        //1
        let cloudSpeed = 60.0 / Double(view.layer.frame.size.width)
        let duration: TimeInterval = Double(view.layer.frame.size.width - layer.frame.origin.x) * cloudSpeed
    
        //2
        let cloudMove = CABasicAnimation(keyPath: "position.x")
        cloudMove.duration = duration
        cloudMove.toValue = self.view.bounds.size.width + layer.bounds.width/2
        cloudMove.delegate = self
        cloudMove.fillMode = kCAFillModeForwards
        cloudMove.setValue("cloud", forKey: "name")
        cloudMove.setValue(layer, forKey: "layer")
        layer.add(cloudMove, forKey: nil)
      }
    
      // MARK: UITextFieldDelegate
    
      func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        let nextField = (textField === username) ? password : username
        nextField?.becomeFirstResponder()
        return true
      }
    
    }
    
    extension ViewController: CAAnimationDelegate {
      func animationDidStop(_ anim: CAAnimation,
                            finished flag: Bool) {
        print("animation did finish")
    
        guard let name = anim.value(forKey: "name") as? String else {
          return
        }
    
        if name == "form" {
          //form field found
    
          let layer = anim.value(forKey: "layer") as? CALayer
          anim.setValue(nil, forKey: "layer")
    
          // let pulse = CABasicAnimation(keyPath: "transform.scale")
         let pulse = CASpringAnimation(keyPath: "transform.scale")
          pulse.damping = 7.5// 阻尼大小,值越大,动画时间越短
          pulse.fromValue = 1.25
          pulse.toValue = 1.0
          // pulse.duration = 0.25
          pulse.duration = pulse.settlingDuration
          layer?.add(pulse, forKey: nil)
        }
    
        if name == "cloud" {
          if let layer = anim.value(forKey: "layer") as? CALayer {
            anim.setValue(nil, forKey: "layer")
    
            layer.position.x = -layer.bounds.width/2
            delay(seconds: 0.5) {
              self.animateCloud(layer: layer)
            }
          }
        }
    
      }
    }
    
    extension ViewController: UITextFieldDelegate {
      func textFieldDidBeginEditing(_ textField: UITextField) {
        guard let runningAnimations = info.layer.animationKeys() else {
          return
        }
        print(runningAnimations)
        info.layer.removeAnimation(forKey: "infoappear")
      }
        
        func textFieldDidEndEditing(_ textField: UITextField) {
            guard let text = textField.text else { return }
            if text.characters.count < 5 {
                // add animations here
                let jump = CASpringAnimation(keyPath: "position.y")
                jump.fromValue = textField.layer.position.y + 1.0
                jump.toValue = textField.layer.position.y
                jump.initialVelocity = 100.0
                jump.mass = 10.0
                jump.stiffness = 1500.0
                jump.damping = 50.0
                jump.duration = jump.settlingDuration
                textField.layer.add(jump, forKey: nil)
                
                textField.layer.borderWidth = 3.0
                textField.layer.borderColor = UIColor.clear.cgColor
                let flash = CASpringAnimation(keyPath: "borderColor")
                flash.damping = 7.0
                flash.stiffness = 200.0
                flash.fromValue = UIColor(red: 1.0, green: 0.27, blue: 0.0, alpha: 1.0).cgColor
                flash.toValue = UIColor.white.cgColor
                flash.duration = flash.settlingDuration
                textField.layer.add(flash, forKey: nil)
                
                textField.layer.cornerRadius = 5
            }
        }
    }
    
    

    参考:

    1. iOS 动画十:Layer Keyframe Animations and Struct Properties

    相关文章

      网友评论

          本文标题:登录页面动画示例

          本文链接:https://www.haomeiwen.com/subject/fuovdrtx.html