iOS: 将 CAShapeLayer 在受约束的子视图中居中
iOS: centering CAShapeLayer inside a constrained subview
我正在尝试在 Circle 的中心渲染一个形状,Circle 是 UIView 的自定义子类(第一个片段)。然后我将 Circle 添加为我的 VC 中的子视图并添加约束(第二个片段)。
当高度和宽度受到限制时,渲染形状的中心(不希望地)位于圆形视图的左上角(如下图)。当仅约束 Circle 视图的 X 和 Y 时,形状如预期的那样位于中心。
为什么会发生这种情况?我该怎么做才能随心所欲地约束 Circle 但形状仍呈现在 Circle 的中心?谢谢
圈子:
class Circle: UIView {
func render(){
let shapeLayer = CAShapeLayer()
let center = self.center
let endAngle = 2 * CGFloat.pi
let circularPath = UIBezierPath(arcCenter: center, radius: 100, startAngle: 0, endAngle: endAngle, clockwise: true)
shapeLayer.path = circularPath.cgPath
self.layer.addSublayer(shapeLayer)
}
}
VC:
class VC: UIViewController {
let circle = Circle()
override func viewDidLoad() {
super.viewDidLoad()
configure()
circle.render()
}
func configure(){
view.backgroundColor = .white
view.addSubview(progressCircle)
progressCircle.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
progressCircle.centerYAnchor.constraint(equalTo: view.centerYAnchor),
progressCircle.centerXAnchor.constraint(equalTo: view.centerXAnchor),
progressCircle.heightAnchor.constraint(equalToConstant: 200),
progressCircle.widthAnchor.constraint(equalToConstant: 200)
])
}
}
结果:
这是固定视图(使用 Xcode 11.4 / Playground 测试)
class Circle: UIView {
let shapeLayer = CAShapeLayer()
override func layoutSubviews() {
super.layoutSubviews()
shapeLayer.position = CGPoint(x: self.bounds.midX, y: self.bounds.midY)
}
func render(){
let center = self.center
let endAngle = 2 * CGFloat.pi
let circularPath = UIBezierPath(arcCenter: center, radius: 100, startAngle: 0, endAngle: endAngle, clockwise: true)
shapeLayer.path = circularPath.cgPath
self.layer.addSublayer(shapeLayer)
}
}
最终得到以下解决方案(这次形状不同,但原理适用)。按预期工作,现在可以根据需要在 VC 中约束和移动视图。
class Circle: UIView {
private var layer: CAShapeLayer!
override func draw(_ rect: CGRect) {
let width = rect.width
let height = rect.height
let lineWidth = 0.1 * min(width, height)
let center = CGPoint(x: width / 2, y: height / 2)
let radius = (min(width, height) - lineWidth) / 2
let startAngle = -CGFloat.pi / 2
let endAngle = startAngle + 2 * CGFloat.pi
let circularPath = UIBezierPath(arcCenter: center, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: true)
layer = CAShapeLayer()
layer.path = circularPath.cgPath
layer.strokeColor = UIColor.lightGray.cgColor
layer.lineWidth = lineWidth
layer.fillColor = UIColor.clear.cgColor
layer.lineCap = .round
layer.addSublayer(backbroundLayer)
}
}
我正在尝试在 Circle 的中心渲染一个形状,Circle 是 UIView 的自定义子类(第一个片段)。然后我将 Circle 添加为我的 VC 中的子视图并添加约束(第二个片段)。
当高度和宽度受到限制时,渲染形状的中心(不希望地)位于圆形视图的左上角(如下图)。当仅约束 Circle 视图的 X 和 Y 时,形状如预期的那样位于中心。
为什么会发生这种情况?我该怎么做才能随心所欲地约束 Circle 但形状仍呈现在 Circle 的中心?谢谢
圈子:
class Circle: UIView {
func render(){
let shapeLayer = CAShapeLayer()
let center = self.center
let endAngle = 2 * CGFloat.pi
let circularPath = UIBezierPath(arcCenter: center, radius: 100, startAngle: 0, endAngle: endAngle, clockwise: true)
shapeLayer.path = circularPath.cgPath
self.layer.addSublayer(shapeLayer)
}
}
VC:
class VC: UIViewController {
let circle = Circle()
override func viewDidLoad() {
super.viewDidLoad()
configure()
circle.render()
}
func configure(){
view.backgroundColor = .white
view.addSubview(progressCircle)
progressCircle.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
progressCircle.centerYAnchor.constraint(equalTo: view.centerYAnchor),
progressCircle.centerXAnchor.constraint(equalTo: view.centerXAnchor),
progressCircle.heightAnchor.constraint(equalToConstant: 200),
progressCircle.widthAnchor.constraint(equalToConstant: 200)
])
}
}
结果:
这是固定视图(使用 Xcode 11.4 / Playground 测试)
class Circle: UIView {
let shapeLayer = CAShapeLayer()
override func layoutSubviews() {
super.layoutSubviews()
shapeLayer.position = CGPoint(x: self.bounds.midX, y: self.bounds.midY)
}
func render(){
let center = self.center
let endAngle = 2 * CGFloat.pi
let circularPath = UIBezierPath(arcCenter: center, radius: 100, startAngle: 0, endAngle: endAngle, clockwise: true)
shapeLayer.path = circularPath.cgPath
self.layer.addSublayer(shapeLayer)
}
}
最终得到以下解决方案(这次形状不同,但原理适用)。按预期工作,现在可以根据需要在 VC 中约束和移动视图。
class Circle: UIView {
private var layer: CAShapeLayer!
override func draw(_ rect: CGRect) {
let width = rect.width
let height = rect.height
let lineWidth = 0.1 * min(width, height)
let center = CGPoint(x: width / 2, y: height / 2)
let radius = (min(width, height) - lineWidth) / 2
let startAngle = -CGFloat.pi / 2
let endAngle = startAngle + 2 * CGFloat.pi
let circularPath = UIBezierPath(arcCenter: center, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: true)
layer = CAShapeLayer()
layer.path = circularPath.cgPath
layer.strokeColor = UIColor.lightGray.cgColor
layer.lineWidth = lineWidth
layer.fillColor = UIColor.clear.cgColor
layer.lineCap = .round
layer.addSublayer(backbroundLayer)
}
}