改变 CAShapeLayer 的边界尺寸高度不会改变实际尺寸,而是改变其位置。

huangapple go评论86阅读模式
英文:

Changing a CAShapeLayer's bounds size height doesn't change the actual size, it changes it's position

问题

A CAShapeLayer is added as a sublayer of a view in a certain position:

// 使用贝塞尔路径创建矩形形状
let barRect = CGRect(x: 0, y: 0, width: 10, height: 100)
let barPath = UIBezierPath(rect: barRect)
let bar = CAShapeLayer()
bar.path = barPath.cgPath
bar.lineWidth = 0.5
// 将锚点放在矩形的左下角
bar.anchorPoint = CGPoint(x: 0, y: 1)
bar.bounds = CGRect(origin: CGPoint.zero, size: barRect.size)
// 在视图中定位矩形
bar.position = CGPoint(x: 0, y: 200)
layer.addSublayer(bar)

Later on, an action attempts to increase the height of the rectangle:

bar.bounds.size.height = 150

When this action is executed, the rectangle changes its position in the view, but not its height. The top of the rectangle moves to where the rectangle should be if the height is increased, but the bottom of the rectangle also moves up, maintaining the original height of the rectangle. What is the problem here? Thanks

英文:

A CAShapeLayer is added as a sublayer of a view in a certain position:

  1. // Use Bezier Path to create a rectangle shape
  2. let barRect = CGRect(x: 0, y: 0, width: 10, height: 100)
  3. let barPath = UIBezierPath(rect: barRect)
  4. let bar = CAShapeLayer()
  5. bar.path = barPath.cgPath
  6. bar.lineWidth = 0.5
  7. // Place anchor on lower left corner of the rectangle
  8. bar.anchorPoint = CGPoint(x: 0, y: 1)
  9. bar.bounds = CGRect(origin: CGPoint.zero, size: barRect.size)
  10. // Position bar in view
  11. bar.position = CGPoint(x: 0, y: 200)
  12. layer.addSublayer(bar)

Later on, an action attempts to increase the height of the rectangle:

  1. bar.bounds.size.height = 150

When this action is executed, the rectangle changes it's position in the view, but not it's height. The top of the rectangle moves to where the rectangle should be if the height is increased, but the bottom of the rectangle also moves up, maintaining the original height of the rectangle. What is the problem here? Thanks

答案1

得分: 2

Changing the layer's bounds does not change the layer's .path.

If your goal is simple rectangles, you can use CALayer instead of CAShapeLayer with a path.

Here's a quick example:

  1. class BarView: UIView {
  2. let bar1 = CAShapeLayer()
  3. let bar2 = CALayer()
  4. override init(frame: CGRect) {
  5. super.init(frame: frame)
  6. commonInit()
  7. }
  8. required init?(coder: NSCoder) {
  9. super.init(coder: coder)
  10. commonInit()
  11. }
  12. func commonInit() -> Void {
  13. let barRect = CGRect(x: 0, y: 0, width: 10, height: 100)
  14. let barPath = UIBezierPath(rect: barRect)
  15. bar1.path = barPath.cgPath
  16. bar1.lineWidth = 0.5
  17. // Place anchor on the lower-left corner of the rectangle
  18. bar1.anchorPoint = CGPoint(x: 0, y: 1)
  19. bar1.bounds = CGRect(origin: CGPoint.zero, size: barRect.size)
  20. // Position bar in view
  21. bar1.position = CGPoint(x: 0, y: 200)
  22. layer.addSublayer(bar1)
  23. bar2.borderWidth = 0.5
  24. // Place the anchor on the lower-left corner of the rectangle
  25. bar2.anchorPoint = CGPoint(x: 0, y: 1)
  26. bar2.bounds = CGRect(origin: CGPoint.zero, size: barRect.size)
  27. // Position the bar in view
  28. bar2.position = CGPoint(x: 40, y: 200)
  29. layer.addSublayer(bar2)
  30. bar1.fillColor = UIColor.red.cgColor
  31. bar1.strokeColor = UIColor.cyan.cgColor
  32. bar2.backgroundColor = UIColor.cyan.cgColor
  33. bar2.borderColor = UIColor.red.cgColor
  34. self.backgroundColor = .yellow
  35. }
  36. override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
  37. // This does not change the layer's PATH
  38. bar1.bounds.size.height = bar1.bounds.size.height == 150 ? 100 : 150
  39. bar2.bounds.size.height = bar2.bounds.size.height == 150 ? 100 : 150
  40. }
  41. }

And a sample controller:

  1. class BarLayerVC: UIViewController {
  2. let someView = BarView()
  3. override func viewDidLoad() {
  4. super.viewDidLoad()
  5. view.backgroundColor = UIColor(white: 0.95, alpha: 1.0)
  6. someView.translatesAutoresizingMaskIntoConstraints = false
  7. view.addSubview(someView)
  8. let g = view.safeAreaLayoutGuide
  9. NSLayoutConstraint.activate([
  10. someView.widthAnchor.constraint(equalToConstant: 100.0),
  11. someView.centerXAnchor.constraint(equalTo: g.centerXAnchor),
  12. someView.heightAnchor.constraint(equalToConstant: 200.0),
  13. someView.centerYAnchor.constraint(equalTo: g.centerYAnchor),
  14. ])
  15. }
  16. }

Tapping the yellow "BarView" will toggle the bar bounds heights between 100 and 150. Red bar is your original CAShapeLayer, and Cyan bar is a CALayer.

英文:

Changing the layer's bounds does not change the layer's .path.

If your goal is simple rectangles, you can use CALayer instead of CAShapeLayer with a path.

Here's a quick example:

  1. class BarView: UIView {
  2. let bar1 = CAShapeLayer()
  3. let bar2 = CALayer()
  4. override init(frame: CGRect) {
  5. super.init(frame: frame)
  6. commonInit()
  7. }
  8. required init?(coder: NSCoder) {
  9. super.init(coder: coder)
  10. commonInit()
  11. }
  12. func commonInit() -&gt; Void {
  13. let barRect = CGRect(x: 0, y: 0, width: 10, height: 100)
  14. let barPath = UIBezierPath(rect: barRect)
  15. bar1.path = barPath.cgPath
  16. bar1.lineWidth = 0.5
  17. // Place anchor on lower left corner of the rectangle
  18. bar1.anchorPoint = CGPoint(x: 0, y: 1)
  19. bar1.bounds = CGRect(origin: CGPoint.zero, size: barRect.size)
  20. // Position bar in view
  21. bar1.position = CGPoint(x: 0, y: 200)
  22. layer.addSublayer(bar1)
  23. bar2.borderWidth = 0.5
  24. // Place anchor on lower left corner of the rectangle
  25. bar2.anchorPoint = CGPoint(x: 0, y: 1)
  26. bar2.bounds = CGRect(origin: CGPoint.zero, size: barRect.size)
  27. // Position bar in view
  28. bar2.position = CGPoint(x: 40, y: 200)
  29. layer.addSublayer(bar2)
  30. bar1.fillColor = UIColor.red.cgColor
  31. bar1.strokeColor = UIColor.cyan.cgColor
  32. bar2.backgroundColor = UIColor.cyan.cgColor
  33. bar2.borderColor = UIColor.red.cgColor
  34. self.backgroundColor = .yellow
  35. }
  36. override func touchesBegan(_ touches: Set&lt;UITouch&gt;, with event: UIEvent?) {
  37. // this does not change the layer&#39;s PATH
  38. bar1.bounds.size.height = bar1.bounds.size.height == 150 ? 100 : 150
  39. bar2.bounds.size.height = bar2.bounds.size.height == 150 ? 100 : 150
  40. }
  41. }

and a sample controller:

  1. class BarLayerVC: UIViewController {
  2. let someView = BarView()
  3. override func viewDidLoad() {
  4. super.viewDidLoad()
  5. view.backgroundColor = UIColor(white: 0.95, alpha: 1.0)
  6. someView.translatesAutoresizingMaskIntoConstraints = false
  7. view.addSubview(someView)
  8. let g = view.safeAreaLayoutGuide
  9. NSLayoutConstraint.activate([
  10. someView.widthAnchor.constraint(equalToConstant: 100.0),
  11. someView.centerXAnchor.constraint(equalTo: g.centerXAnchor),
  12. someView.heightAnchor.constraint(equalToConstant: 200.0),
  13. someView.centerYAnchor.constraint(equalTo: g.centerYAnchor),
  14. ])
  15. }
  16. }

Tapping the yellow "BarView" will toggle the bar bounds heights between 100 and 150 ... Red bar is your original CAShapeLayer and Cyan bar is a CALayer:

改变 CAShapeLayer 的边界尺寸高度不会改变实际尺寸,而是改变其位置。 改变 CAShapeLayer 的边界尺寸高度不会改变实际尺寸,而是改变其位置。


Edit

Here's that same BarView class, but with a 3rd (green) bar. It uses a CAShapeLayer and updates its .path in layoutSubviews():

  1. class BarView: UIView {
  2. let bar1 = CAShapeLayer()
  3. let bar2 = CALayer()
  4. let bar3 = CAShapeLayer()
  5. override init(frame: CGRect) {
  6. super.init(frame: frame)
  7. commonInit()
  8. }
  9. required init?(coder: NSCoder) {
  10. super.init(coder: coder)
  11. commonInit()
  12. }
  13. func commonInit() -&gt; Void {
  14. let barRect = CGRect(x: 0, y: 0, width: 10, height: 100)
  15. let barPath = UIBezierPath(rect: barRect)
  16. bar1.path = barPath.cgPath
  17. bar1.lineWidth = 0.5
  18. // Place anchor on lower left corner of the rectangle
  19. bar1.anchorPoint = CGPoint(x: 0, y: 1)
  20. bar1.bounds = CGRect(origin: CGPoint.zero, size: barRect.size)
  21. // Position bar in view
  22. bar1.position = CGPoint(x: 0, y: 200)
  23. layer.addSublayer(bar1)
  24. bar2.borderWidth = 0.5
  25. // Place anchor on lower left corner of the rectangle
  26. bar2.anchorPoint = CGPoint(x: 0, y: 1)
  27. bar2.bounds = CGRect(origin: CGPoint.zero, size: barRect.size)
  28. // Position bar in view
  29. bar2.position = CGPoint(x: 40, y: 200)
  30. layer.addSublayer(bar2)
  31. bar1.fillColor = UIColor.red.cgColor
  32. bar1.strokeColor = UIColor.cyan.cgColor
  33. bar2.backgroundColor = UIColor.cyan.cgColor
  34. bar2.borderColor = UIColor.red.cgColor
  35. bar3.fillColor = UIColor.green.cgColor
  36. bar3.strokeColor = UIColor.blue.cgColor
  37. bar3.lineWidth = 0.5
  38. // Place anchor on lower left corner of the rectangle
  39. bar3.anchorPoint = CGPoint(x: 0, y: 1)
  40. bar3.bounds = CGRect(origin: CGPoint.zero, size: barRect.size)
  41. // Position bar in view
  42. bar3.position = CGPoint(x: 80, y: 200)
  43. layer.addSublayer(bar3)
  44. self.backgroundColor = .yellow
  45. }
  46. override func layoutSubviews() {
  47. super.layoutSubviews()
  48. let barRect = CGRect(x: 0, y: 0, width: 10, height: bar3.bounds.height)
  49. let barPath = UIBezierPath(roundedRect: barRect, cornerRadius: 4.0)
  50. bar3.path = barPath.cgPath
  51. }
  52. override func touchesBegan(_ touches: Set&lt;UITouch&gt;, with event: UIEvent?) {
  53. // this does not change the layer&#39;s PATH
  54. bar1.bounds.size.height = bar1.bounds.size.height == 150 ? 100 : 150
  55. bar2.bounds.size.height = bar2.bounds.size.height == 150 ? 100 : 150
  56. bar3.bounds.size.height = bar3.bounds.size.height == 150 ? 100 : 150
  57. }
  58. }

改变 CAShapeLayer 的边界尺寸高度不会改变实际尺寸,而是改变其位置。 改变 CAShapeLayer 的边界尺寸高度不会改变实际尺寸,而是改变其位置。

I changed the path for "bar3" to a roundedRect so we can see why we might want to use a CAShapeLayer.

huangapple
  • 本文由 发表于 2023年3月4日 02:11:20
  • 转载请务必保留本文链接:https://go.coder-hub.com/75630530.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定