Mar*_*mi1 6 uitextfield cabasicanimation ios uibezierpath swift
我正在尝试将动画添加到UITextField用户编辑的边框.
想法是在编辑第一个文本字段后在登录页面中显示线条动画,然后在用户切换到下一个文本字段后,该行应移动到下面的文本字段.
我做了一个不能像我期待的那样努力的尝试.
我的代码:
class ViewController: UIViewController, UITextFieldDelegate {
@IBOutlet weak var verticSpace: NSLayoutConstraint!
@IBOutlet weak var usernameTxtField: UITextField!
@IBOutlet weak var passwordTxtField: UITextField!
weak var shapeLayer: CAShapeLayer?
let path = UIBezierPath()
let shapeLayerNew = CAShapeLayer()
override func viewDidLoad() {
super.viewDidLoad()
usernameTxtField.delegate = self
passwordTxtField.delegate = self
}
func textFieldDidBeginEditing(_ textField: UITextField) {
let path = UIBezierPath()
if textField == usernameTxtField {
if textField.text == "" {
self.startMyLine()
}
}
if passwordTxtField.isFirstResponder {
let path2 = UIBezierPath()
path2.move(to: CGPoint(x: usernameTxtField.frame.width, y: usernameTxtField.frame.height - shapeLayerNew.lineWidth))
path2.addLine(to: CGPoint(x: usernameTxtField.frame.width, y: (usernameTxtField.frame.height - shapeLayerNew.lineWidth) + passwordTxtField.frame.height + verticSpace.constant))
path2.addLine(to: CGPoint(x: 0, y: (usernameTxtField.frame.height - shapeLayerNew.lineWidth) + passwordTxtField.frame.height + verticSpace.constant))
let combinedPath = path.cgPath.mutableCopy()
combinedPath?.addPath(path2.cgPath)
shapeLayerNew.path = path2.cgPath
let startAnimation = CABasicAnimation(keyPath: "strokeStart")
startAnimation.fromValue = 0
startAnimation.toValue = 0.8
let endAnimation = CABasicAnimation(keyPath: "strokeEnd")
endAnimation.fromValue = 0.2
endAnimation.toValue = 1.0
let animation = CAAnimationGroup()
animation.animations = [startAnimation, endAnimation]
animation.duration = 2
shapeLayerNew.add(animation, forKey: "MyAnimation")
}
}
func startMyLine() {
self.shapeLayer?.removeFromSuperlayer()
// create whatever path you want
shapeLayerNew.fillColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 0).cgColor
shapeLayerNew.strokeColor = #colorLiteral(red: 1, green: 0, blue: 0, alpha: 1).cgColor
shapeLayerNew.lineWidth = 4
path.move(to: CGPoint(x: 0, y: usernameTxtField.frame.height - shapeLayerNew.lineWidth))
path.addLine(to: CGPoint(x: usernameTxtField.frame.width, y: usernameTxtField.frame.height - shapeLayerNew.lineWidth))
// create shape layer for that path
shapeLayerNew.path = path.cgPath
// animate it
usernameTxtField.layer.addSublayer(shapeLayerNew)
let animation = CABasicAnimation(keyPath: "strokeEnd")
animation.fromValue = 0
animation.duration = 2
shapeLayerNew.add(animation, forKey: "MyAnimation")
// save shape layer
self.shapeLayer = shapeLayerNew
}
}
Run Code Online (Sandbox Code Playgroud)
我的结果:
预期结果:
编辑1:
我已根据@SWAT答案应用了这些更改,但我仍然无法获得预期的结果.当正在编辑用户名的字段时,我会显示四行,而它只应在移动到下一个文本字段时出现,然后在动画结束后四行应该消失.
我的更新代码:
class ViewController: UIViewController, UITextFieldDelegate {
@IBOutlet weak var usernameTxtField: UITextField!
@IBOutlet weak var passwordTxtField: UITextField!
weak var shapeLayer: CAShapeLayer?
let path = UIBezierPath()
let shapeLayerNew = CAShapeLayer()
var animLayer = CAShapeLayer()
override func viewDidLoad() {
super.viewDidLoad()
usernameTxtField.delegate = self
passwordTxtField.delegate = self
}
func textFieldDidBeginEditing(_ textField: UITextField) {
if textField == usernameTxtField{
var path = UIBezierPath()
path.move(to: CGPoint.init(x: self.usernameTxtField.frame.minX, y: self.usernameTxtField.frame.maxY))
path.addLine(to: CGPoint.init(x: self.usernameTxtField.frame.maxX, y: self.usernameTxtField.frame.maxY))
path.addQuadCurve(to: CGPoint.init(x: self.passwordTxtField.frame.maxX, y: self.passwordTxtField.frame.maxY), controlPoint: CGPoint.init(x: self.usernameTxtField.frame.maxX, y: self.usernameTxtField.frame.maxY))
path.addLine(to: CGPoint.init(x: self.passwordTxtField.frame.minX, y: self.passwordTxtField.frame.maxY))
animLayer.fillColor = UIColor.clear.cgColor
animLayer.path = path.cgPath
animLayer.strokeColor = UIColor.cyan.cgColor
animLayer.lineWidth = 3.0
self.view.layer.addSublayer(animLayer)
animLayer.strokeEnd = 0
animLayer.strokeStart = 0
let initialAnimation = CABasicAnimation(keyPath: "strokeEnd")
initialAnimation.toValue = 0.5
initialAnimation.beginTime = 0
initialAnimation.duration = 0.5
initialAnimation.fillMode = kCAFillModeBoth
initialAnimation.isRemovedOnCompletion = false
animLayer.add(initialAnimation, forKey: "usernameFieldStrokeEnd")
let secondTextFieldAnimStrokeStart = CABasicAnimation(keyPath: "strokeStart")
secondTextFieldAnimStrokeStart.toValue = 0
secondTextFieldAnimStrokeStart.beginTime = 0
secondTextFieldAnimStrokeStart.duration = 0.5
secondTextFieldAnimStrokeStart.fillMode = kCAFillModeBoth
secondTextFieldAnimStrokeStart.isRemovedOnCompletion = false
animLayer.add(secondTextFieldAnimStrokeStart, forKey: "usernameFieldStrokeStart")
} else {
var path = UIBezierPath()
path.move(to: CGPoint.init(x: self.usernameTxtField.frame.minX, y: self.usernameTxtField.frame.maxY))
path.addLine(to: CGPoint.init(x: self.usernameTxtField.frame.maxX, y: self.usernameTxtField.frame.maxY))
path.addQuadCurve(to: CGPoint.init(x: self.passwordTxtField.frame.maxX, y: self.passwordTxtField.frame.maxY), controlPoint: CGPoint.init(x: self.usernameTxtField.frame.maxX, y: self.usernameTxtField.frame.maxY))
path.addLine(to: CGPoint.init(x: self.passwordTxtField.frame.minX, y: self.passwordTxtField.frame.maxY))
animLayer.fillColor = UIColor.clear.cgColor
animLayer.path = path.cgPath
animLayer.strokeColor = UIColor.cyan.cgColor
animLayer.lineWidth = 3.0
self.view.layer.addSublayer(animLayer)
animLayer.strokeEnd = 0
animLayer.strokeStart = 0
let secondTextFieldAnimStrokeEnd = CABasicAnimation(keyPath: "strokeEnd")
secondTextFieldAnimStrokeEnd.toValue = 1.0
secondTextFieldAnimStrokeEnd.beginTime = 0
secondTextFieldAnimStrokeEnd.duration = 0.5
secondTextFieldAnimStrokeEnd.fillMode = kCAFillModeBoth
secondTextFieldAnimStrokeEnd.isRemovedOnCompletion = false
animLayer.add(secondTextFieldAnimStrokeEnd, forKey: "secondTextFieldStrokeEnd")
let secondTextFieldAnimStrokeStart = CABasicAnimation(keyPath: "strokeStart")
secondTextFieldAnimStrokeStart.toValue = 0.5
secondTextFieldAnimStrokeStart.beginTime = 0
secondTextFieldAnimStrokeStart.duration = 0.5
secondTextFieldAnimStrokeStart.fillMode = kCAFillModeBoth
secondTextFieldAnimStrokeStart.isRemovedOnCompletion = false
animLayer.add(secondTextFieldAnimStrokeStart, forKey: "secondTextFieldStrokeStart")
}
}
Run Code Online (Sandbox Code Playgroud)
}
这就是我现在得到的:
编辑2:
我设法找到一种方式,给了我一个相当接近的结果,我期待.我已将isRemoveCompletion设置为true,以便在动画完成时擦除线条,然后在文本字段中添加底部边框.
class ViewController: UIViewController, UITextFieldDelegate {
@IBOutlet weak var usernameTxtField: UITextField!
@IBOutlet weak var passwordTxtField: UITextField!
var animLayer = CAShapeLayer()
let newLayer2 = CAShapeLayer()
override func viewDidLoad() {
super.viewDidLoad()
usernameTxtField.delegate = self
passwordTxtField.delegate = self
makePath()
}
func makePath(){
//var path = UIBezierPath()
let path = UIBezierPath()
path.move(to: CGPoint.init(x: self.usernameTxtField.frame.minX, y: self.usernameTxtField.frame.maxY))
path.addLine(to: CGPoint.init(x: self.usernameTxtField.frame.maxX, y: self.usernameTxtField.frame.maxY))
path.addQuadCurve(to: CGPoint.init(x: self.passwordTxtField.frame.maxX, y: self.passwordTxtField.frame.maxY), controlPoint: CGPoint.init(x: self.usernameTxtField.frame.maxX, y: self.usernameTxtField.frame.maxY))
path.addLine(to: CGPoint.init(x: self.passwordTxtField.frame.minX, y: self.passwordTxtField.frame.maxY))
animLayer.fillColor = UIColor.clear.cgColor
animLayer.path = path.cgPath
animLayer.strokeColor = UIColor(red: 214/255, green: 54/255, blue: 57/255, alpha: 1).cgColor
animLayer.lineWidth = 3.0
animLayer.lineCap = kCALineCapRound
animLayer.lineJoin = kCALineJoinRound
self.view.layer.addSublayer(animLayer)
animLayer.strokeEnd = 0
animLayer.strokeStart = 0
}
func addBottomBorder(textField: UITextField) {
var path = UIBezierPath()
path.move(to: CGPoint.init(x: textField.frame.minX, y: textField.frame.maxY))
path.addLine(to: CGPoint.init(x: textField.frame.maxX, y: textField.frame.maxY))
self.newLayer2.fillColor = UIColor.clear.cgColor
self.newLayer2.path = path.cgPath
self.newLayer2.strokeColor = UIColor(red: 214/255, green: 54/255, blue: 57/255, alpha: 1).cgColor
self.newLayer2.lineWidth = 3.0
self.newLayer2.lineCap = kCALineCapRound
self.newLayer2.lineJoin = kCALineJoinRound
self.view.layer.addSublayer(self.newLayer2)
}
func textFieldDidBeginEditing(_ textField: UITextField) {
if textField == usernameTxtField{
CATransaction.begin()
self.newLayer2.removeFromSuperlayer()
let initialAnimation = CABasicAnimation(keyPath: "strokeEnd")
initialAnimation.toValue = 0.45
initialAnimation.beginTime = 0
initialAnimation.duration = 1.0
initialAnimation.fillMode = kCAFillModeBoth
initialAnimation.isRemovedOnCompletion = true
initialAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
animLayer.add(initialAnimation, forKey: "usernameFieldStrokeEnd")
let secondTextFieldAnimStrokeStart = CABasicAnimation(keyPath: "strokeStart")
secondTextFieldAnimStrokeStart.toValue = 0
secondTextFieldAnimStrokeStart.beginTime = 0
secondTextFieldAnimStrokeStart.duration = 1.0
secondTextFieldAnimStrokeStart.fillMode = kCAFillModeBoth
secondTextFieldAnimStrokeStart.isRemovedOnCompletion = true
secondTextFieldAnimStrokeStart.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
CATransaction.setCompletionBlock {
if !self.passwordTxtField.isFirstResponder {
self.addBottomBorder(textField: self.usernameTxtField)
}
}
animLayer.add(secondTextFieldAnimStrokeStart, forKey: "usernameFieldStrokeStart")
CATransaction.commit()
} else {
CATransaction.begin()
self.newLayer2.removeFromSuperlayer()
let secondTextFieldAnimStrokeEnd = CABasicAnimation(keyPath: "strokeEnd")
secondTextFieldAnimStrokeEnd.toValue = 1.0
secondTextFieldAnimStrokeEnd.beginTime = 0
secondTextFieldAnimStrokeEnd.duration = 1.0
secondTextFieldAnimStrokeEnd.fillMode = kCAFillModeBoth
secondTextFieldAnimStrokeEnd.isRemovedOnCompletion = true
secondTextFieldAnimStrokeEnd.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
animLayer.add(secondTextFieldAnimStrokeEnd, forKey: "secondTextFieldStrokeEnd")
let secondTextFieldAnimStrokeStart = CABasicAnimation(keyPath: "strokeStart")
secondTextFieldAnimStrokeStart.toValue = 0.5
secondTextFieldAnimStrokeStart.beginTime = 0
secondTextFieldAnimStrokeStart.duration = 1.0
secondTextFieldAnimStrokeStart.fillMode = kCAFillModeBoth
secondTextFieldAnimStrokeStart.isRemovedOnCompletion = true
secondTextFieldAnimStrokeStart.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
CATransaction.setCompletionBlock {
if !self.usernameTxtField.isFirstResponder {
self.addBottomBorder(textField: self.passwordTxtField)
}
}
animLayer.add(secondTextFieldAnimStrokeStart, forKey: "secondTextFieldStrokeStart")
CATransaction.commit()
}
}
Run Code Online (Sandbox Code Playgroud)
你想要的动画非常酷。不过,实施起来还需要做很多工作。我已经完成了相当多的核心动画,创建整个动画序列可能需要我几天的时间。
核心动画路径动画的基本规则是起始路径和结束路径必须具有相同数量和类型的控制点。您需要将动画分成多个片段并分别为它们设置动画。
对于某些部分(形状不会改变,但您可以向路径添加/删除像素,就像您用钢笔绘制和/或擦除之前绘制的部分一样),您将拥有固定的路径并为其设置strokeStart动画strokeEnd特性。
对于动画的其他部分(形状发生变化的地方),您必须仔细构建具有相同数量和类型的控制点以及所需的开始和结束形状的开始和结束路径,并在它们之间进行动画处理。(这可能意味着对于某些动画,您创建的起始或结束路径具有许多子路径,这些子路径实际上绘制了更简单的形状。)需要花很多心思才能弄清楚如何做到这一点。
第一步是绘制动画图并将其分成几个阶段。