如何在 UITableViewCell 中居中自定义 CAShapeLayer?
How to center a custom CAShapeLayer inside UITableViewCell?
我使用 CAShapeLayer
创建了一个自定义的循环进度条。我可以使用 view.center
轻松地将它置于任何 UIView
的中心,但我需要将它置于 UITableView
单元格的中心,这就是我得到的:
如果您看一下该图像,上面的圆圈正确地显示了位于 UIView
中心的相同 CAShapeLayer
。
我试过了view.center, [x: view.frame.width /2, y :view.frame.height / 2 ]
我还尝试将 UIView
置于 Cell
的中心,并将 CAShapeLayer
添加到该居中的视图中……没用。
let center = CGPoint(x: self.frame.width/2, y: self.frame.height/2)
let circularPath = UIBezierPath(arcCenter: .zero, radius: 80, startAngle: 0, endAngle: CGFloat.pi * 2, clockwise: true)
pulsatingLayer.position = center
完整代码:
import UIKit
class CurrentGACell: UITableViewCell {
let shapeLayer = CAShapeLayer()
var pulsatingLayer: CAShapeLayer!
let percentageLabel: UILabel = {
var label = UILabel()
label.text = "Start"
label.textAlignment = .center
label.font = UIFont(name: "AvenirNext-DemiBold", size: 30)
label.backgroundColor = UIColor.clear
return label
}()
@IBOutlet weak var mainView: UIView!
override func awakeFromNib() {
super.awakeFromNib()
setupNotificationObserver()
let center = self.contentView.center //CGPoint(x: contentView.frame.width/2, y: contentView.frame.height/2) //mainView.center
let circularPath = UIBezierPath(arcCenter: .zero, radius: 80, startAngle: 0, endAngle: CGFloat.pi * 2, clockwise: true)
//pulsating
pulsatingLayer = CAShapeLayer()
pulsatingLayer.path = circularPath.cgPath
pulsatingLayer.fillColor = UIColor.AppColors.Blue.withAlphaComponent(0.2).cgColor
pulsatingLayer.strokeColor = UIColor.clear.cgColor
pulsatingLayer.lineWidth = 10
pulsatingLayer.lineCap = .round
pulsatingLayer.position = center
self.contentView.layer.addSublayer(pulsatingLayer)
animatePulsatingLayer()
//trackLayer
let trackLayer = CAShapeLayer()
trackLayer.path = circularPath.cgPath
trackLayer.fillColor = UIColor.white.cgColor
trackLayer.strokeColor = UIColor.AppColors.Blue.withAlphaComponent(0.2).cgColor
trackLayer.lineWidth = 10
trackLayer.lineCap = .round
trackLayer.position = center
self.contentView.layer.addSublayer(trackLayer)
//shapeLayer
shapeLayer.path = circularPath.cgPath
shapeLayer.fillColor = UIColor.white.cgColor
shapeLayer.strokeColor = UIColor.AppColors.Blue.cgColor
shapeLayer.lineWidth = 10
shapeLayer.strokeEnd = 0
shapeLayer.lineCap = .round
shapeLayer.position = center
shapeLayer.transform = CATransform3DMakeRotation(-CGFloat.pi/2, 0, 0, 1)
self.contentView.layer.addSublayer(shapeLayer)
self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleTap)))
self.contentView.addSubview(percentageLabel)
percentageLabel.frame = CGRect(x: 0, y: 0, width: 500, height: 100)
percentageLabel.center = center
}
@objc private func handleTap() {
//beginDownloading()
animateCircle()
}
fileprivate func animateCircle() {
print("animating....")
let basicAnimation = CABasicAnimation(keyPath: "strokeEnd")
basicAnimation.toValue = 0.65
basicAnimation.duration = 1
basicAnimation.fillMode = CAMediaTimingFillMode.forwards
basicAnimation.isRemovedOnCompletion = false
percentageLabel.text = "29w + 3d"
shapeLayer.add(basicAnimation, forKey: "urSoBasic")
}
private func animatePulsatingLayer() {
let animation = CABasicAnimation(keyPath: "transform.scale")
animation.toValue = 1.2
animation.duration = 1
animation.autoreverses = true
animation.repeatCount = Float.infinity
animation.timingFunction = CAMediaTimingFunction(name: .easeOut)
pulsatingLayer.add(animation, forKey: "pulsating")
}
private func setupNotificationObserver() {
NotificationCenter.default.addObserver(self, selector: #selector(handleEnterForegroung), name: UIApplication.willEnterForegroundNotification, object: nil)
}
@objc func handleEnterForegroung() {
animatePulsatingLayer()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
将依赖.contentView
框架的代码移至layoutSubviews()
。只有这样你的 CurrentGACell
才能保证有合适的框架。
类似于:
let shapeLayer = CAShapeLayer()
let trackLayer = CAShapeLayer()
var pulsatingLayer: CAShapeLayer!
func layoutSubviews() {
super.layoutSubviews()
let center = contentView.center
pulsatingLayer.position = center
trackLayer.position = center
shapeLayer.position = center
percentageLabel.center = center
}
您需要在 layoutSubviews
方法中添加所有代码。
let shapeLayer = CAShapeLayer()
override func layoutSubviews() {
let circularPath = UIBezierPath(arcCenter: .zero, radius: 25, startAngle: 0, endAngle: CGFloat.pi * 2, clockwise: true)
shapeLayer.path = circularPath.cgPath
shapeLayer.strokeColor = UIColor.blue.cgColor
shapeLayer.fillColor = UIColor.gray.cgColor
shapeLayer.lineWidth = 1.0
shapeLayer.position = self.contentView.center
if !self.contentView.layer.sublayers!.contains(shapeLayer){
self.layer.addSublayer(shapeLayer)
}
}
或
您可以使用 UIScreen
的静态值
override func awakeFromNib() {
super.awakeFromNib()
let point = CGPoint.init(x: UIScreen.main.bounds.width/2, y: self.contentView.frame.height/2)
let circularPath = UIBezierPath(arcCenter: .zero, radius: 25, startAngle: 0, endAngle: CGFloat.pi * 2, clockwise: true)
let shapeLayer = CAShapeLayer()
shapeLayer.path = circularPath.cgPath
shapeLayer.strokeColor = UIColor.blue.cgColor
shapeLayer.fillColor = UIColor.gray.cgColor
shapeLayer.lineWidth = 1.0
shapeLayer.position = point
self.layer.addSublayer(shapeLayer)
}
注:
您得到了不可预测的结果,因为您在帧渲染之前添加了一个层。
我使用 CAShapeLayer
创建了一个自定义的循环进度条。我可以使用 view.center
轻松地将它置于任何 UIView
的中心,但我需要将它置于 UITableView
单元格的中心,这就是我得到的:
如果您看一下该图像,上面的圆圈正确地显示了位于 UIView
中心的相同 CAShapeLayer
。
我试过了view.center, [x: view.frame.width /2, y :view.frame.height / 2 ]
我还尝试将 UIView
置于 Cell
的中心,并将 CAShapeLayer
添加到该居中的视图中……没用。
let center = CGPoint(x: self.frame.width/2, y: self.frame.height/2)
let circularPath = UIBezierPath(arcCenter: .zero, radius: 80, startAngle: 0, endAngle: CGFloat.pi * 2, clockwise: true)
pulsatingLayer.position = center
完整代码:
import UIKit
class CurrentGACell: UITableViewCell {
let shapeLayer = CAShapeLayer()
var pulsatingLayer: CAShapeLayer!
let percentageLabel: UILabel = {
var label = UILabel()
label.text = "Start"
label.textAlignment = .center
label.font = UIFont(name: "AvenirNext-DemiBold", size: 30)
label.backgroundColor = UIColor.clear
return label
}()
@IBOutlet weak var mainView: UIView!
override func awakeFromNib() {
super.awakeFromNib()
setupNotificationObserver()
let center = self.contentView.center //CGPoint(x: contentView.frame.width/2, y: contentView.frame.height/2) //mainView.center
let circularPath = UIBezierPath(arcCenter: .zero, radius: 80, startAngle: 0, endAngle: CGFloat.pi * 2, clockwise: true)
//pulsating
pulsatingLayer = CAShapeLayer()
pulsatingLayer.path = circularPath.cgPath
pulsatingLayer.fillColor = UIColor.AppColors.Blue.withAlphaComponent(0.2).cgColor
pulsatingLayer.strokeColor = UIColor.clear.cgColor
pulsatingLayer.lineWidth = 10
pulsatingLayer.lineCap = .round
pulsatingLayer.position = center
self.contentView.layer.addSublayer(pulsatingLayer)
animatePulsatingLayer()
//trackLayer
let trackLayer = CAShapeLayer()
trackLayer.path = circularPath.cgPath
trackLayer.fillColor = UIColor.white.cgColor
trackLayer.strokeColor = UIColor.AppColors.Blue.withAlphaComponent(0.2).cgColor
trackLayer.lineWidth = 10
trackLayer.lineCap = .round
trackLayer.position = center
self.contentView.layer.addSublayer(trackLayer)
//shapeLayer
shapeLayer.path = circularPath.cgPath
shapeLayer.fillColor = UIColor.white.cgColor
shapeLayer.strokeColor = UIColor.AppColors.Blue.cgColor
shapeLayer.lineWidth = 10
shapeLayer.strokeEnd = 0
shapeLayer.lineCap = .round
shapeLayer.position = center
shapeLayer.transform = CATransform3DMakeRotation(-CGFloat.pi/2, 0, 0, 1)
self.contentView.layer.addSublayer(shapeLayer)
self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleTap)))
self.contentView.addSubview(percentageLabel)
percentageLabel.frame = CGRect(x: 0, y: 0, width: 500, height: 100)
percentageLabel.center = center
}
@objc private func handleTap() {
//beginDownloading()
animateCircle()
}
fileprivate func animateCircle() {
print("animating....")
let basicAnimation = CABasicAnimation(keyPath: "strokeEnd")
basicAnimation.toValue = 0.65
basicAnimation.duration = 1
basicAnimation.fillMode = CAMediaTimingFillMode.forwards
basicAnimation.isRemovedOnCompletion = false
percentageLabel.text = "29w + 3d"
shapeLayer.add(basicAnimation, forKey: "urSoBasic")
}
private func animatePulsatingLayer() {
let animation = CABasicAnimation(keyPath: "transform.scale")
animation.toValue = 1.2
animation.duration = 1
animation.autoreverses = true
animation.repeatCount = Float.infinity
animation.timingFunction = CAMediaTimingFunction(name: .easeOut)
pulsatingLayer.add(animation, forKey: "pulsating")
}
private func setupNotificationObserver() {
NotificationCenter.default.addObserver(self, selector: #selector(handleEnterForegroung), name: UIApplication.willEnterForegroundNotification, object: nil)
}
@objc func handleEnterForegroung() {
animatePulsatingLayer()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
将依赖.contentView
框架的代码移至layoutSubviews()
。只有这样你的 CurrentGACell
才能保证有合适的框架。
类似于:
let shapeLayer = CAShapeLayer()
let trackLayer = CAShapeLayer()
var pulsatingLayer: CAShapeLayer!
func layoutSubviews() {
super.layoutSubviews()
let center = contentView.center
pulsatingLayer.position = center
trackLayer.position = center
shapeLayer.position = center
percentageLabel.center = center
}
您需要在 layoutSubviews
方法中添加所有代码。
let shapeLayer = CAShapeLayer()
override func layoutSubviews() {
let circularPath = UIBezierPath(arcCenter: .zero, radius: 25, startAngle: 0, endAngle: CGFloat.pi * 2, clockwise: true)
shapeLayer.path = circularPath.cgPath
shapeLayer.strokeColor = UIColor.blue.cgColor
shapeLayer.fillColor = UIColor.gray.cgColor
shapeLayer.lineWidth = 1.0
shapeLayer.position = self.contentView.center
if !self.contentView.layer.sublayers!.contains(shapeLayer){
self.layer.addSublayer(shapeLayer)
}
}
或
您可以使用 UIScreen
override func awakeFromNib() {
super.awakeFromNib()
let point = CGPoint.init(x: UIScreen.main.bounds.width/2, y: self.contentView.frame.height/2)
let circularPath = UIBezierPath(arcCenter: .zero, radius: 25, startAngle: 0, endAngle: CGFloat.pi * 2, clockwise: true)
let shapeLayer = CAShapeLayer()
shapeLayer.path = circularPath.cgPath
shapeLayer.strokeColor = UIColor.blue.cgColor
shapeLayer.fillColor = UIColor.gray.cgColor
shapeLayer.lineWidth = 1.0
shapeLayer.position = point
self.layer.addSublayer(shapeLayer)
}
注:
您得到了不可预测的结果,因为您在帧渲染之前添加了一个层。