问题描述
我想使用名为 UIViewController
的 UIView
在 LoadingView
上显示视图。但是,每当我尝试使用以下代码时,只会出现 UIView
而没有 CALayer
。
理想情况下,此代码会在调用 UIView
时导致出现 startLoading()
。然后 CALayer
将被添加到 UIView
中,它将是一个围绕视图中心旋转的圆。我可以更改什么才能使此 CALayer
以正确的方式显示在 UIView
上?
class LoadingView: UIView {
let circleLayer = CAShapeLayer()
init() {
super.init(frame: .zero)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func startLoading() {
let size = 50
if let topView = UIApplication.topViewController()?.view {
topView.addSubview(self)
translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
self.centerYAnchor.constraint(equalTo: topView.centerYAnchor),self.centerXAnchor.constraint(equalTo: topView.centerXAnchor),self.widthAnchor.constraint(equalToConstant: CGFloat(size)),self.heightAnchor.constraint(equalToConstant: CGFloat(size)),])
self.layer.cornerRadius = frame.height/3
addShadow(shadowColor: UIColor.label.cgColor,shadowOffset: CGSize(width: 0,height: 0),shadowOpacity: 0.3,shadowRadius: 2)
backgroundColor = .secondarySystemBackground
animateCircle(duration: .infinity)
}
}
private func animateCircle(duration: TimeInterval) {
let circlePath = UIBezierPath(arcCenter: CGPoint(x: self.frame.midX,y: self.frame.midY),radius: (frame.size.width),startAngle: 0.0,endAngle: CGFloat(Double.pi*2),clockwise: true)
// Setup the CAShapeLayer with the path,colors,and line width
circleLayer.path = circlePath.cgPath
circleLayer.fillColor = UIColor.clear.cgColor
circleLayer.strokeColor = Constants.Colors.mainBlue?.cgColor
circleLayer.linewidth = 1.0
// Don't draw the circle initially
circleLayer.strokeEnd = 0.0
// Add the circleLayer to the view's layer's sublayers
self.layer.addSublayer(circleLayer)
// We want to animate the strokeEnd property of the circleLayer
let animation = CABasicAnimation(keyPath: "strokeEnd")
animation.duration = duration
animation.fromValue = 0
animation.tovalue = 1
animation.timingFunction = camediatimingFunction(name: camediatimingFunctionName.eaSEOut)
circleLayer.strokeEnd = 0
circleLayer.add(animation,forKey: "animateCircle")
let rotationAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
rotationAnimation.fromValue = 0.0
rotationAnimation.tovalue = Double.pi*3
rotationAnimation.duration = duration
rotationAnimation.timingFunction = camediatimingFunction(name: camediatimingFunctionName.easeIn)
circleLayer.add(rotationAnimation,forKey: nil)
let fadeAnimation = CABasicAnimation(keyPath: "opacity")
fadeAnimation.fromValue = 1
fadeAnimation.tovalue = 0
fadeAnimation.duration = duration
fadeAnimation.timingFunction = camediatimingFunction(name: camediatimingFunctionName.easeIn)
circleLayer.add(fadeAnimation,forKey: nil)
}
func stopLoading() {
self.removeFromSuperview()
}
}
解决方法
有些地方出了问题:
-
CALayer的
frame
/bounds
需要设置准确。请记住,这可能会改变其初始位置。例如,在我的第一次测试中,图层认为它是一个0x0
矩形。 -
必须正确设置曲线的路径(与#1相关)
-
您当前的
.infinity
时间会使动画需要无限长的时间才能完成。我认为你想要的是一个需要一定时间并无限重复的动画。
我现在可能忘记了另一个更改,但这应该可以让您开始:
class LoadingView: UIView {
let circleLayer = CAShapeLayer()
private var circlePath : UIBezierPath = .init()
let size : CGFloat = 50
init() {
super.init(frame: .zero)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func startLoading() {
if let topView = UIApplication.topViewController()?.view {
topView.addSubview(self)
translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
self.centerYAnchor.constraint(equalTo: topView.centerYAnchor),self.centerXAnchor.constraint(equalTo: topView.centerXAnchor),self.widthAnchor.constraint(equalToConstant: CGFloat(size)),self.heightAnchor.constraint(equalToConstant: CGFloat(size)),])
self.layer.cornerRadius = frame.height/3
backgroundColor = .secondarySystemBackground
self.layer.addSublayer(circleLayer)
calculateCirclePath()
animateCircle(duration: 1,repeats: true)
}
}
func calculateCirclePath() {
self.circlePath = UIBezierPath(arcCenter: CGPoint(x: size / 2,y: size / 2),radius: size / 2,startAngle: 0.0,endAngle: CGFloat(Double.pi*2),clockwise: true)
}
override func layoutSubviews() {
circleLayer.frame = self.layer.bounds
}
private func animateCircle(duration: TimeInterval,repeats: Bool) {
// Setup the CAShapeLayer with the path,colors,and line width
circleLayer.path = circlePath.cgPath
circleLayer.fillColor = UIColor.clear.cgColor
circleLayer.strokeColor = UIColor.blue.cgColor
circleLayer.lineWidth = 1.0
// Don't draw the circle initially
circleLayer.strokeEnd = 0.0
// Add the circleLayer to the view's layer's sublayers
self.layer.addSublayer(circleLayer)
// We want to animate the strokeEnd property of the circleLayer
let animation = CABasicAnimation(keyPath: "strokeEnd")
animation.duration = duration
animation.repeatCount = repeats ? .infinity : 1
animation.fromValue = 0
animation.toValue = 1
animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut)
circleLayer.strokeEnd = 0
circleLayer.add(animation,forKey: "animateCircle")
let rotationAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
rotationAnimation.repeatCount = repeats ? .infinity : 1
rotationAnimation.fromValue = 0.0
rotationAnimation.toValue = Double.pi*3
rotationAnimation.duration = duration
rotationAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeIn)
circleLayer.add(rotationAnimation,forKey: nil)
let fadeAnimation = CABasicAnimation(keyPath: "opacity")
fadeAnimation.repeatCount = repeats ? .infinity : 1
fadeAnimation.fromValue = 1
fadeAnimation.toValue = 0
fadeAnimation.duration = duration
fadeAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeIn)
circleLayer.add(fadeAnimation,forKey: nil)
}
func stopLoading() {
self.removeFromSuperview()
}
}
,
我注意到您没有设置 circleLayer 的 frame
。
尝试将其设置为您的视图。