- 小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
下面是想要实现的动画效果:
Logo的动画做完了,接下来要做中间画线的动画以及中间Button的浮现动画 同样的,在这里声明loginButton属性和所需要的button大小属性。
private let loginButtonSize = CGSize(width: 60, height: 60)
var loginButton: UIButton!
在setupUI里面设置好loginButton的大小、位置、border、cornerRadius、title等属性并添加到为view的subview。
loginButton = UIButton(type: .custom)
loginButton.frame = CGRect(x: view.frame.width/2 - loginButtonSize.width/2,
y: view.frame.height/2 - loginButtonSize.height/2,
width: loginButtonSize.width,
height: loginButtonSize.height)
loginButton.center = view.center
loginButton.layer.borderColor = UIColor.brown.cgColor
loginButton.layer.borderWidth = 1
loginButton.layer.cornerRadius = loginButtonSize.width / 2
loginButton.setTitle("Open", for: .normal)
loginButton.setTitleColor(.brown, for: .normal)
loginButton.setTitleColor(UIColor.brown.withAlphaComponent(0.5), for: .highlighted)
loginButton.alpha = 0
view.addSubview(loginButton)
接下来就需要先画线,用UIBezierPath来画出所期望的线。
let path = UIBezierPath()
// Start at center left of screen
path.move(to: CGPoint(x: 0, y: view.center.y))
// Add line to the left side -10 px of login button
path.addLine(to: CGPoint(x: loginButton.center.x - loginButtonSize.width - 10,
y: view.center.y))
// Add arc that go to -10 px bottom of login button
path.addArc(withCenter: loginButton.center,
radius: loginButtonSize.height/2 + 10,
startAngle: CGFloat(Double.pi),
endAngle: CGFloat(Double.pi/2),
clockwise: false)
// Add arc that go to 10 px right of login button
path.addArc(withCenter: loginButton.center,
radius: loginButtonSize.height/2 + 10,
startAngle: CGFloat(Double.pi/2),
endAngle: CGFloat(0),
clockwise: false)
// Add line to the center right of screen
path.addLine(to: CGPoint(x: view.frame.width,
y: view.center.y))
接下来根据画的线来创建shapeLayer并设置好各种属性。
let shapeLayer = CAShapeLayer()
shapeLayer.path = path.cgPath
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.strokeColor = UIColor.brown.cgColor
shapeLayer.lineWidth = 1
shapeLayer.lineCap = CAShapeLayerLineCap.round
shapeLayer.lineJoin = CAShapeLayerLineJoin.round
shapeLayer.strokeEnd = 1
view.layer.addSublayer(shapeLayer)
现在的效果:
接下来只需要简单的为其添加一个动画就可以了,这里需要把前面的shapeLayer.strokeEnd 改为0。
let animation = CABasicAnimation(keyPath: "strokeEnd")
animation.toValue = 1
animation.duration = 2.0
animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
animation.fillMode = CAMediaTimingFillMode.both // keep to value after finishing
animation.isRemovedOnCompletion = false // don't remove after finishing
shapeLayer.add(animation, forKey: animation.keyPath)
到这里,画线的动画就完成啦。就剩下button的浮现动画啦。那么这里有个问题,button的浮现动画需要在画线的动画之后进行,那么如何确保button的浮现动画在画线的动画之后进行呢?这里就需要用到CATransaction。代码如下:
CATransaction.begin()
CATransaction.setCompletionBlock({
UIView.animate(withDuration: 0.5, animations: {
self.loginButton.alpha = 1.0
})
})
shapeLayer.add(animation, forKey: animation.keyPath)
CATransaction.commit()
到这里, 登陆页面动画的logo部分以及中间部分的动画都已经完成了,也就是2/3的工作已经完成了,接下来就只剩下🚗 的动画啦。 现在的效果:
完整代码:
import UIKit
class ViewController: UIViewController {
private let logoSize = CGSize(width: 250, height: 40)
private var logoImageView: UIImageView!
private let loginButtonSize = CGSize(width: 60, height: 60)
var loginButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
setupUI()
animateLogo {
self.animateLoginButton()
}
}
private func setupUI() {
// Initialize the logo at the center of screen
logoImageView = UIImageView(frame: CGRect(x: view.frame.width/2 - logoSize.width/2,
y: view.frame.height/2 - logoSize.height/2,
width: logoSize.width,
height: logoSize.height))
logoImageView.contentMode = .scaleToFill
logoImageView.image = UIImage(named: "logo_icon")
logoImageView.isUserInteractionEnabled = true
logoImageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(shakeLogo)))
view.addSubview(logoImageView)
// Initialize the logo at the center of screen
loginButton = UIButton(type: .custom)
loginButton.frame = CGRect(x: view.frame.width/2 - loginButtonSize.width/2,
y: view.frame.height/2 - loginButtonSize.height/2,
width: loginButtonSize.width,
height: loginButtonSize.height)
loginButton.center = view.center
loginButton.layer.borderColor = UIColor.brown.cgColor
loginButton.layer.borderWidth = 1
loginButton.layer.cornerRadius = loginButtonSize.width / 2
loginButton.setTitle("Open", for: .normal)
loginButton.setTitleColor(.brown, for: .normal)
loginButton.setTitleColor(UIColor.brown.withAlphaComponent(0.5), for: .highlighted)
loginButton.alpha = 0
view.addSubview(loginButton)
}
@objc private func shakeLogo() {
let animation = CABasicAnimation(keyPath: "position")
animation.duration = 0.07
animation.repeatCount = 4
animation.autoreverses = true
animation.fromValue = NSValue(cgPoint: CGPoint(x: logoImageView.center.x - 10, y: logoImageView.center.y))
animation.toValue = NSValue(cgPoint: CGPoint(x: logoImageView.center.x + 10, y: logoImageView.center.y))
logoImageView.layer.add(animation, forKey: "position")
}
private func animateLoginButton() {
// Step 1: Draw the Bezier path
let path = UIBezierPath()
// Start at center left of screen
path.move(to: CGPoint(x: 0, y: view.center.y))
// Add line to the left side -10 px of login button
path.addLine(to: CGPoint(x: loginButton.center.x - loginButtonSize.width - 10,
y: view.center.y))
// Add arc that go to -10 px bottom of login button
path.addArc(withCenter: loginButton.center,
radius: loginButtonSize.height/2 + 10,
startAngle: CGFloat(Double.pi),
endAngle: CGFloat(Double.pi/2),
clockwise: false)
// Add arc that go to 10 px right of login button
path.addArc(withCenter: loginButton.center,
radius: loginButtonSize.height/2 + 10,
startAngle: CGFloat(Double.pi/2),
endAngle: CGFloat(0),
clockwise: false)
// Add line to the center right of screen
path.addLine(to: CGPoint(x: view.frame.width,
y: view.center.y))
// Step 2: Create the shape layer from Bezier path
let shapeLayer = CAShapeLayer()
shapeLayer.path = path.cgPath
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.strokeColor = UIColor.brown.cgColor
shapeLayer.lineWidth = 1
shapeLayer.lineCap = CAShapeLayerLineCap.round
shapeLayer.lineJoin = CAShapeLayerLineJoin.round
shapeLayer.strokeEnd = 0
view.layer.addSublayer(shapeLayer)
let animation = CABasicAnimation(keyPath: "strokeEnd")
animation.toValue = 1
animation.duration = 2.0
animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
animation.fillMode = CAMediaTimingFillMode.both // keep to value after finishing
animation.isRemovedOnCompletion = false // don't remove after finishing
CATransaction.begin()
CATransaction.setCompletionBlock({
UIView.animate(withDuration: 0.5, animations: {
self.loginButton.alpha = 1.0
})
})
shapeLayer.add(animation, forKey: animation.keyPath)
CATransaction.commit()
}
private func animateLogo(completion: @escaping ()->()) {
UIView.animate(withDuration: 1.0, animations: {
self.logoImageView.frame = self.logoImageView.frame.offsetBy(dx: 0, dy: -250)
}, completion: { _ in
completion()
})
}
}