问题描述
我正在尝试以编程方式绘制一个圆进度视图,并将其居中于子视图 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:
- 如果视图由视图控制器拥有,则
UIView
将viewWillLayoutSubviews
发送到其视图控制器. -
UIView
发送自身layoutSubviews
. - 如果视图由视图控制器拥有,则
UIView
将viewDidLayoutSubviews
发送到其视图控制器.
- The
UIView
sendsviewWillLayoutSubviews
to its view controller, if the view is owned by a view controller. - The
UIView
sends itselflayoutSubviews
. - The
UIView
sendsviewDidLayoutSubviews
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:
-
覆盖B的
layoutSubviews
.由于B是C的直接父视图,因此可以确定B的layoutSubviews
调用super.layoutSubviews()
后,C的框架已更新.
Override B's
layoutSubviews
. Since B is the direct superview of C, you can be sure that after B'slayoutSubviews
callssuper.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:
- 覆盖C的
layoutSubviews
.如果C
更改大小,则将调用此方法.如果C改变位置但大小不变,则不一定会调用它.
- Override C's
layoutSubviews
. This will be called ifC
changes size. It won't necessarily be called if C changes position but stays the same size.
这篇关于子视图何时完全正确地布置?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!