本文介绍了子视图何时完全正确地布置?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

限时删除!!

我正在尝试以编程方式绘制一个圆进度视图,并将其居中于子视图 circleView 中,该视图已在界面构建器中设置/约束.但是,我不确定何时可以访问 circleView 的最终大小和中心(我正在使用自动布局),而最终需要绘制该圆圈.这是涉及的代码:

I am trying to programmatically draw a circle progress view and center it within a subview circleView, which I have set up/constrained in the interface builder. However, I am not sure when circleView's final size and center will be accessible (I'm using auto layout), which I ultimately need to draw the circle. Here's the involved code:

@IBOutlet weak var circleView: UIView!

let circleShapeLayer = CAShapeLayer()
let trackLayer = CAShapeLayer()

override func viewDidLoad() {
    super.viewDidLoad()
    // createCircle()
}

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    print(circleView.frame.size.width)
    createCircle()
}

func createCircle() {

    // Draw/modify circle

    let center = circleView.center

    // Where I need to use circleView's width/center
    let circularPath = UIBezierPath(arcCenter: center, radius: circleView.frame.size.width, startAngle: -CGFloat.pi/2, endAngle: 2 * CGFloat.pi, clockwise: true)

    trackLayer.path = circularPath.cgPath
    trackLayer.strokeColor = UIColor.lightGray.cgColor
    trackLayer.lineWidth = 10
    trackLayer.fillColor = UIColor.clear.cgColor

    circleView.layer.addSublayer(trackLayer)

    circleShapeLayer.path = circularPath.cgPath
    circleShapeLayer.strokeColor = Colors.tintColor.cgColor
    circleShapeLayer.lineWidth = 10
    circleShapeLayer.fillColor = UIColor.clear.cgColor
    circleShapeLayer.lineCap = kCALineCapRound
    // circleShapeLayer.strokeEnd = 0

    circleView.layer.addSublayer(circleShapeLayer)

}

这将两次打印 circleView 的宽度,并且仅在第二次运行 viewDidLayoutSubviews()时才真正正确:

This prints the width of circleView twice, and only on the second run of viewDidLayoutSubviews() is it actually correct:

207.0
187.5 // Correct (width of entire view is 375)

但是,圆两次都错误地沿着相同的精确路径绘制,这使我感到困惑,因为宽度如上所述变化.也许我在想这是错误的方式?

However, the circle draws incorrectly along the same exact path both times, which baffles me because the width changes as shown above. Maybe I'm thinking about this the wrong way?

我宁愿不要画两次圆,而是希望有一种方法可以在 viewDidLoad()中运行 createCircle()给我相同的结果.任何帮助将不胜感激.

I'd rather not draw the circle twice and was hoping there would a way to run createCircle() within viewDidLoad() instead, but at the moment this just gives me the same result. Any help would be very much appreciated.

推荐答案

@rmaddy的评论正确:处理此问题的最佳方法是使用自定义视图来管理 trackLayer circleShapeLayer.覆盖自定义视图的 layoutSubviews 以设置图层的框架和/或路径.

@rmaddy's comment is correct: The best way to handle this is to use a custom view to manage trackLayer and circleShapeLayer. Override the custom view's layoutSubviews to set the frame and/or path of the layers.

也就是说,我将回答您所说的子视图何时完整,正确布置?"

That said, I'll answer your stated question of "When are subviews completely, correctly laid out?"

考虑此视图层次结构:

A
|
+--- B
|    |
|    +--- C
|    |
|    +--- D
|
+--- E
     |
     +--- F
     |
     +--- G

在运行循环的布局阶段,Core Animation以深度优先的顺序遍历图层层次,以查找需要布局的图层.因此,Core Animation首先访问A的层,然后访问B的层,然后访问C的层,然后访问D的层,然后访问E的层,然后访问F的层,然后访问G的层.

During the layout phase of the run loop, Core Animation traverses the layer hierarchy in depth-first order, looking for layers that need layout. So Core Animation visits A's layer first, then B's layer, then C's layer, then D's layer, then E's layer, then F's layer, then G's layer.

如果图层需要布局(其 needsLayout 属性为true),则Core Animation将 layoutSublayers 发送到该图层.默认情况下,图层通过将 layoutSublayersOfLayer:发送给其委托来处理此问题.通常,委托是拥有该图层的 UIView .

If a layer needs layout (its needsLayout property is true), then Core Animation sends layoutSublayers to the layer. A layer handles this by default by sending layoutSublayersOfLayer: to its delegate. Usually the delegate is the UIView that owns the layer.

默认情况下, UIView 通过(除其他外)发送三个消息来处理 layoutSublayersOfLayer::

By default, a UIView handles layoutSublayersOfLayer: by (among other things) sending three messages:

  1. 如果视图由视图控制器拥有,则 UIView viewWillLayoutSubviews 发送到其视图控制器.
  2. UIView 发送自身 layoutSubviews .
  3. 如果视图由视图控制器拥有,则 UIView viewDidLayoutSubviews 发送到其视图控制器.
  1. The UIView sends viewWillLayoutSubviews to its view controller, if the view is owned by a view controller.
  2. The UIView sends itself layoutSubviews.
  3. The UIView sends viewDidLayoutSubviews to its view controller, if the view is owned by a view controller.

-[UIView layoutSubviews] 的默认实现中,该视图根据自动布局约束设置其每个直接子视图的框架.

In the default implementation of -[UIView layoutSubviews], the view sets the frame of each of its direct subviews, based on auto layout constraints.

请注意,在 layoutSubviews 中,视图仅设置其直接子视图的框架.因此,例如,A仅设置B和E的帧.设置C,D,F和G的帧.

Note that in layoutSubviews, a view only sets the frames of its direct subviews. So for example, A only sets the frames of B and E. It does not set the frames of C, D, F, and G.

因此,我们假设A是视图控制器的视图,但其他视图均不属于视图控制器.

So let's suppose A is the view of a view controller, but none of the other views are owned by a view controller.

当A处理 layoutSubviews 时,它将设置B和E的帧.然后将 viewDidLayoutSubviews 发送到其视图控制器.C,D,F和G的框架目前尚未更新.视图控制器不能假定C,D,F和G在其 viewDidLayoutSubviews 中具有正确的帧.

When A's handles layoutSubviews, it sets the frames of B and E. Then it sends viewDidLayoutSubviews to its view controller. The frames of C, D, F, and G have not been updated at this point. The view controller cannot assume that C, D, F, and G have correct frames in its viewDidLayoutSubviews.

有两个不错的放置代码的地方,这些代码肯定会在C的框架已更新时运行:

There are two good places to put code that will run when C's frame has definitely been updated:

  1. 覆盖B的 layoutSubviews .由于B是C的直接父视图,因此可以确定B的 layoutSubviews 调用 super.layoutSubviews()后,C的框架已更新.

  1. Override B's layoutSubviews. Since B is the direct superview of C, you can be sure that after B's layoutSubviews calls super.layoutSubviews(), C's frame has been updated.

放置一个由B负责的视图控制器.即,使B为某个视图控制器的视图.然后,在拥有B的视图控制器中覆盖 viewDidLayoutSubviews .

Put a view controller in charge of B. That is, make B be the view of some view controller. Then, override viewDidLayoutSubviews in the view controller that owns B.

如果只需要确定C的大小何时已更新,则可以选择第三种方法:

If you only need to know when C's size has definitely been updated, you have a third option:

  1. 覆盖C的 layoutSubviews .如果 C 更改大小,则将调用此方法.如果C改变位置但大小不变,则不一定会调用它.
  1. Override C's layoutSubviews. This will be called if C changes size. It won't necessarily be called if C changes position but stays the same size.

这篇关于子视图何时完全正确地布置?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

1403页,肝出来的..

09-06 11:54