为什么我的 CaLayer 没有显示在 UIView 之上?
why isnt my CaLayer showing up on top of the UIView?
我想使用名为 LoadingView
的 UIView
在 UIViewController
上显示一个视图。但是,每当我尝试使用以下代码时,只有 UIView
出现而没有 CALayer
.
理想情况下,此代码会导致在调用 startLoading()
时出现 UIView
。然后 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()
}
}
我注意到您没有设置 circleLayer 的 frame
。
尝试将其设置为您的视图。
有些地方出了问题:
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()
}
}
我想使用名为 LoadingView
的 UIView
在 UIViewController
上显示一个视图。但是,每当我尝试使用以下代码时,只有 UIView
出现而没有 CALayer
.
理想情况下,此代码会导致在调用 startLoading()
时出现 UIView
。然后 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()
}
}
我注意到您没有设置 circleLayer 的 frame
。
尝试将其设置为您的视图。
有些地方出了问题:
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()
}
}