重读此 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
}
}
}
参考:
网友评论