ran*_*lan 4 uibezierpath autolayout swift
我正在尝试使用CAShapeLayer绘制圆形按钮并为其设置动画,但是仅该绘制让我很头疼-我似乎无法弄清楚如何将数据传递到类中。
这是我的设置:-会绘制CAShapeLayer的UIView类型的类-该视图在我的视图控制器中呈现,并使用自动布局约束构建
我尝试使用layoutIfNeeded,但似乎传递数据的时间太晚,无法绘制视图。我也尝试过在vieWillLayoutSubviews()中重画视图,但是什么也没有。下面的示例代码。我究竟做错了什么?
我是否太早/太迟地传递数据?我绘制bezierPath太晚了吗?
我非常感谢指针。
也许还有第二个后续问题:是否有一种更简单的方法来绘制与其视图大小绑定的圆形路径?
在我的View Controller中:
import UIKit
class ViewController: UIViewController {
let buttonView: CircleButton = {
let view = CircleButton()
view.backgroundColor = .black
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
override func viewWillLayoutSubviews() {
}
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(buttonView)
buttonView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
buttonView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
buttonView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.75).isActive = true
buttonView.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.25).isActive = true
buttonView.layoutIfNeeded()
buttonView.arcCenter = buttonView.center
buttonView.radius = buttonView.frame.width/2
}
override func viewDidAppear(_ animated: Bool) {
print(buttonView.arcCenter)
print(buttonView.radius)
}
}
Run Code Online (Sandbox Code Playgroud)
以及buttonView的类:
class CircleButton: UIView {
//Casting outer circular layers
let trackLayer = CAShapeLayer()
var arcCenter = CGPoint()
var radius = CGFloat()
//UIView Init
override init(frame: CGRect) {
super.init(frame: frame)
}
//UIView post init
override func layoutSubviews() {
super.layoutSubviews()
print("StudyButtonView arcCenter \(arcCenter)")
print("StudyButtonView radius \(radius)")
layer.addSublayer(trackLayer)
let outerCircularPath = UIBezierPath(arcCenter: arcCenter, radius: radius, startAngle: 0, endAngle: 2*CGFloat.pi, clockwise: true)
trackLayer.path = outerCircularPath.cgPath
trackLayer.strokeColor = UIColor.lightGray.cgColor
trackLayer.lineWidth = 5
trackLayer.strokeStart = 0
trackLayer.strokeEnd = 1
trackLayer.fillColor = UIColor.clear.cgColor
trackLayer.transform = CATransform3DMakeRotation(-CGFloat.pi/2, 0, 0, 1)
}
//Required for subclass
required init?(coder aDecoder: NSCoder) {
fatalError("has not been implemented")
}
}
Run Code Online (Sandbox Code Playgroud)
是不是真的有自动布局和正确实施的之间的任何关联CircleButton类。您的CircleButton班级不知道或不在乎它是通过自动布局配置的,还是具有固定大小的。
你的自动布局代码看起来OK(超过5分和6以下等)。您的代码段中的大多数问题都在您的CircleButton课程中。一些观察:
如果要旋转形状图层,则也必须设置其形状frame,否则尺寸为.zero,最终将围绕origin视图的角度旋转形状(并在视图的外侧旋转bounds,如果您认为这是问题所在)在剪辑子视图)。确保设置frame的CAShapeLayer是bounds视图试图转动它。坦白说,我将删除transform,但是鉴于您正在使用strokeStartand strokeEnd,所以我猜您可能稍后要更改这些值,并使其从12点开始,在这种情况下,转换是有意义的。
底线(如果旋转)设置第frame一个。如果不是,则设置图层的frame是可选的。
如果要更改视图的属性以更新形状层,则需要确保didSet观察者对形状层进行了适当的更新(或调用setNeedsLayout)。您不希望您的视图控制器不必弄乱形状层的内部结构,但还希望确保这些更改确实反映在形状层中。
这只是次要的观察,但我建议在添加形状层的过程中init仅配置一次并将其添加到视图层次结构中。这样更有效。因此,让各种init方法调用您自己的configure方法。然后,在中进行大小相关的操作(例如更新路径)layoutSubviews。最后,让属性观察器直接更新形状层。这种分工更加有效。
如果你愿意,你可以做这个@IBDesignable,把它放在自己的目标在你的项目。然后,您可以在IB中直接添加它,并查看其外观。您还可以设置所有各种属性@IBInspectable,也可以在IB中正确设置它们。然后,如果您不想这样做,则无需在视图控制器的代码中执行任何操作。(但是,如果您愿意,请放心。)
这是一个小问题,但是当您以编程方式添加视图时,无需调用buttonView.layoutIfNeeded()。仅在设置约束动画时才需要执行此操作,而您在这里没有这样做。添加约束(并解决上述问题)后,按钮将正确布置,而无需明确layoutIfNeeded要求。
您的视图控制器有一行代码,内容为:
buttonView.arcCenter = buttonView.center
Run Code Online (Sandbox Code Playgroud)
那就是混合arcCenter(这是buttonView的坐标空间内的坐标)和buttonView.center(这是视图控制器的根视图的坐标空间内按钮中心的坐标)。一个与另一个无关。就个人而言,我会摆脱这种手动设置的arcCenter,而是必须layoutSubviews在ButtonView打理这个动态,使用bounds.midX和bounds.midY。
综合所有这些,您将得到类似以下内容的信息:
@IBDesignable
class CircleButton: UIView {
private let trackLayer = CAShapeLayer()
@IBInspectable var lineWidth: CGFloat = 5 { didSet { updatePath() } }
@IBInspectable var fillColor: UIColor = .clear { didSet { trackLayer.fillColor = fillColor.cgColor } }
@IBInspectable var strokeColor: UIColor = .lightGray { didSet { trackLayer.strokeColor = strokeColor.cgColor } }
@IBInspectable var strokeStart: CGFloat = 0 { didSet { trackLayer.strokeStart = strokeStart } }
@IBInspectable var strokeEnd: CGFloat = 1 { didSet { trackLayer.strokeEnd = strokeEnd } }
override init(frame: CGRect) {
super.init(frame: frame)
configure()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
configure()
}
private func configure() {
trackLayer.fillColor = fillColor.cgColor
trackLayer.strokeColor = strokeColor.cgColor
trackLayer.strokeStart = strokeStart
trackLayer.strokeEnd = strokeEnd
layer.addSublayer(trackLayer)
}
override func layoutSubviews() {
super.layoutSubviews()
updatePath()
}
private func updatePath() {
let arcCenter = CGPoint(x: bounds.midX, y: bounds.midY)
let radius = (min(bounds.width, bounds.height) - lineWidth) / 2
trackLayer.lineWidth = lineWidth
trackLayer.path = UIBezierPath(arcCenter: arcCenter, radius: radius, startAngle: 0, endAngle: 2 * .pi, clockwise: true).cgPath
// There's no need to rotate it if you're drawing a complete circle.
// But if you're going to transform, set the `frame`, too.
trackLayer.transform = CATransform3DIdentity
trackLayer.frame = bounds
trackLayer.transform = CATransform3DMakeRotation(-.pi / 2, 0, 0, 1)
}
}
Run Code Online (Sandbox Code Playgroud)
产生:

或者,你可以调整权在IB设置,你会看到它生效:

并且确保所有didSet观察者都ButtonView可以更新路径或直接更新某些形状图层的属性之后,视图控制器现在可以更新这些属性,并将它们自动呈现在中ButtonView。