【发布时间】:2020-12-07 08:08:48
【问题描述】:
我目前正在玩CALayers。出于演示目的,我正在创建一个自定义 UIView 来呈现电量计。该视图有两个子层:
- 一个用于背景
- 手用一个
然后,代表手的图层会简单地相应旋转以指向正确的值。到目前为止,一切都很好。现在我希望视图在视图大小发生变化时调整其图层的大小。为此,我创建了 layoutSubviews 方法的覆盖,如下所示:
public override func layoutSubviews()
{
super.layoutSubviews()
if previousBounds == nil || !previousBounds!.equalTo(self.bounds)
{
previousBounds = self.bounds
self.updateLayers(self.bounds)
}
}
由于该方法被多次调用,我使用previousBounds 来确保仅在大小实际发生变化时才对图层执行更新。
一开始,我在updateLayers方法中只有以下代码来设置子层的框架:
backgroundLayer.frame = bounds.insetBy(dx: 5, dy: 5)
handLayer.frame = bounds.insetBy(dx: 5, dy: 5)
效果很好 - 直到 handLayer 被旋转。在那种情况下,它的大小会发生一些奇怪的事情。我想这是因为框架是在旋转之后应用的,当然,旋转后的图层实际上并不适合边界,因此会调整大小以适应。
我目前的解决方案是临时创建一个新的CATransaction 来抑制动画,将转换恢复为身份,设置框架,然后像这样重新应用转换:
CATransaction.begin()
CATransaction.setDisableActions(true)
let oldTransform = scaleLayer.transform
handLayer.transform = CATransform3DIdentity
handLayer.frame = bounds.insetBy(dx: 5, dy: 5)
handLayer.transform = oldTransform
CATransaction.commit()
我已经尝试省略CATransaction,而是将handLayer.affineTransform 应用于我正在设置的bounds,但这并没有产生预期的结果——也许我做错了(附带问题:如何旋转一个给定的CGRect 在它的中心周围而不自己做所有的数学)?
我的问题很简单:是否有推荐的方法是设置转换层的框架,或者我找到的解决方案已经是“解决方案”了?
编辑
Kevvv 提供了一些示例代码,我对其进行了修改以演示我的问题:
class ViewController: UIViewController {
let customView = CustomView(frame: .init(origin: .init(x: 200, y: 200), size: .init(width: 200, height: 200)))
let backgroundLayer = CALayer()
let handLayer = CAShapeLayer()
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(customView)
customView.backgroundColor = UIColor.blue
backgroundLayer.backgroundColor = UIColor.yellow.cgColor
backgroundLayer.frame = customView.bounds
let handPath = UIBezierPath()
handPath.move(to: backgroundLayer.position)
handPath.addLine(to: .init(x: 0, y: backgroundLayer.position.y))
handLayer.frame = customView.bounds
handLayer.path = handPath.cgPath
handLayer.strokeColor = UIColor.black.cgColor
handLayer.backgroundColor = UIColor.red.cgColor
customView.layer.addSublayer(backgroundLayer)
customView.layer.addSublayer(handLayer)
handLayer.transform = CATransform3DMakeRotation(5, 0, 0, 1)
let tap = UITapGestureRecognizer(target: self, action: #selector(tapped))
customView.addGestureRecognizer(tap)
}
@objc func tapped(_ sender: UITapGestureRecognizer) {
customView.frame = customView.frame.insetBy(dx:10, dy:10)
/*let animation = CABasicAnimation(keyPath: #keyPath(CALayer.transform))
let fromValue = self.handLayer.transform
let toValue = CGFloat.pi * 2
animation.duration = 2
animation.fromValue = fromValue
animation.toValue = toValue
animation.valueFunction = CAValueFunction(name: .rotateZ)
self.handLayer.add(animation, forKey: nil)*/
}
}
class CustomView: UIView {
var previousBounds: CGRect!
override func layoutSubviews() {
super.layoutSubviews()
if previousBounds == nil || !previousBounds!.equalTo(self.bounds) {
previousBounds = self.bounds
self.updateLayers(self.bounds)
}
}
func updateLayers(_ bounds: CGRect) {
guard let sublayers = self.layer.sublayers else { return }
for sublayer in sublayers {
sublayer.frame = bounds.insetBy(dx: 5, dy: 5)
}
}
}
如果您将它添加到游乐场,然后运行并点击控件,您就会明白我的意思了。注意红色的“正方形”。
【问题讨论】: