在 viewDidLoad() 中获取视图的渲染边界
Get rendered bounds of a view in viewDidLoad()
我整天都在寻求这方面的帮助,但找不到答案。
我有一个子视图,我正在其中绘制一个矩形,该矩形由子视图框起来,所以我需要知道自动布局调整的子视图的大小才能正确地框住它。我无法从 ViewDidLoad()
找到执行此操作的方法,以便在启动时正确呈现矩形。我尝试了以下方法:
- 在
viewDidLoad()
中绘制矩形之前使用 dayView.setNeedsLayout()
后跟 dayView.layoutIfNeeded()
但是检查这些语句的任一侧显示 dayView.bounds 未更改。
- 从
viewDidLayoutSubviews()
绘制视图,这有效,但导致我的矩形被绘制 5 次,因为每个重绘的子视图(我有 5 个)调用 viewDidLayoutSubviews()
(包含矩形的相关子视图在 5 次调用中的第 4 次调用时重绘)——这似乎是在浪费资源,肯定有更好的方法吗?
- 在
ViewDidLoad()
内绘制视图两次,希望第一次强制绘制会导致视图调整大小,以便第二次绘制可以访问第一次绘制后的新边界(我知道很绝望,但是它仍然不起作用)。
希望有人能帮忙。
func drawGradient(object: UIView, rect: CGRect, slackX: Int, gradWidth: Int, yPos: Int) -> Void {
// the rectangle width and height set to fit within view with x & y border.
let gradientView = UIView(frame: rect)
let gradient = CAGradientLayer()
gradient.frame = gradientView.frame
gradient.colors = getDayGradientLocations().gradientCol
gradient.locations = getDayGradientLocations().gradientLoc
dayView.layer.addSublayer(gradient)
let civilDawn = getTimeAsProportionOfDay(time: tides.civilDawn) * Double(rect.height) + Double(yPos)
let path = UIBezierPath()
path.move(to: CGPoint(x: slackX, y: Int(civilDawn)))
path.addLine(to: CGPoint(x: slackX + Int(rect.width), y: Int(civilDawn)))
let shapeLayer = CAShapeLayer()
shapeLayer.path = path.cgPath
shapeLayer.strokeColor = UIColor.red.cgColor
shapeLayer.lineWidth = CGFloat(2)
let civilDusk = getTimeAsProportionOfDay(time: tides.civilDusk) * Double(rect.height) + Double(yPos)
path.move(to: CGPoint(x: slackX, y: Int(civilDusk)))
path.addLine(to: CGPoint(x: slackX + Int(rect.width), y: Int(civilDusk)))
shapeLayer.path = path.cgPath
shapeLayer.strokeColor = UIColor.red.cgColor
shapeLayer.lineWidth = CGFloat(2)
object.layer.addSublayer(shapeLayer)
drawLabel(object: object, rect: rect, slackX: slackX, offset: 15, time: tides.civilDawn)
drawLabel(object: object, rect: rect, slackX: slackX, offset: -15, time: tides.civilDusk)
}
转到 viewDidLoad 中的主线程并执行操作将解决您的问题
DispatchQueue.main.async {
// here you can add anything it will have proper frame
}
一些观察:
如果您在视图控制器中调整视图的 frame
,正确的位置是 viewDidLayoutSubviews
。是的,这被调用了很多次,但它通常不会对性能产生任何可观察到的影响。
我不会建议 setNeedsLayout
、layoutIfNeeded
或 viewDidLoad
中的任何那些极其脆弱的技术。如果您要在视图控制器中执行此操作,viewDidLayoutSubviews
是正确的位置。
通常,如果要自定义子视图绘制和子视图布局,您会在视图的 layoutSubviews
中执行此操作,而不是在任何视图控制器方法中执行此操作。
同样,如果你正在做任何手动绘图,你会把它放在 UIView
子类(或任何相关的 CALayer
子类)中,而不是视图控制器。
更好的是,如果可以的话,最好让自动布局系统为您处理,而不是手动调整框架。如果您发现自己手动调整了 frame
,通常会有更好的模式。
FWIW,您可以根据其子视图的大小(或固有大小)定义容器视图的约束(并设置相关视图的内容拥抱和压缩阻力)。我们经常将自动布局视为自上而下的引擎,但它可以双向工作。
如果您向我们展示您的“绘图”代码 and/or 一两个屏幕快照,我们可能会提供更具体的建议。
我整天都在寻求这方面的帮助,但找不到答案。
我有一个子视图,我正在其中绘制一个矩形,该矩形由子视图框起来,所以我需要知道自动布局调整的子视图的大小才能正确地框住它。我无法从 ViewDidLoad()
找到执行此操作的方法,以便在启动时正确呈现矩形。我尝试了以下方法:
- 在
viewDidLoad()
中绘制矩形之前使用dayView.setNeedsLayout()
后跟dayView.layoutIfNeeded()
但是检查这些语句的任一侧显示 dayView.bounds 未更改。 - 从
viewDidLayoutSubviews()
绘制视图,这有效,但导致我的矩形被绘制 5 次,因为每个重绘的子视图(我有 5 个)调用viewDidLayoutSubviews()
(包含矩形的相关子视图在 5 次调用中的第 4 次调用时重绘)——这似乎是在浪费资源,肯定有更好的方法吗? - 在
ViewDidLoad()
内绘制视图两次,希望第一次强制绘制会导致视图调整大小,以便第二次绘制可以访问第一次绘制后的新边界(我知道很绝望,但是它仍然不起作用)。
希望有人能帮忙。
func drawGradient(object: UIView, rect: CGRect, slackX: Int, gradWidth: Int, yPos: Int) -> Void {
// the rectangle width and height set to fit within view with x & y border.
let gradientView = UIView(frame: rect)
let gradient = CAGradientLayer()
gradient.frame = gradientView.frame
gradient.colors = getDayGradientLocations().gradientCol
gradient.locations = getDayGradientLocations().gradientLoc
dayView.layer.addSublayer(gradient)
let civilDawn = getTimeAsProportionOfDay(time: tides.civilDawn) * Double(rect.height) + Double(yPos)
let path = UIBezierPath()
path.move(to: CGPoint(x: slackX, y: Int(civilDawn)))
path.addLine(to: CGPoint(x: slackX + Int(rect.width), y: Int(civilDawn)))
let shapeLayer = CAShapeLayer()
shapeLayer.path = path.cgPath
shapeLayer.strokeColor = UIColor.red.cgColor
shapeLayer.lineWidth = CGFloat(2)
let civilDusk = getTimeAsProportionOfDay(time: tides.civilDusk) * Double(rect.height) + Double(yPos)
path.move(to: CGPoint(x: slackX, y: Int(civilDusk)))
path.addLine(to: CGPoint(x: slackX + Int(rect.width), y: Int(civilDusk)))
shapeLayer.path = path.cgPath
shapeLayer.strokeColor = UIColor.red.cgColor
shapeLayer.lineWidth = CGFloat(2)
object.layer.addSublayer(shapeLayer)
drawLabel(object: object, rect: rect, slackX: slackX, offset: 15, time: tides.civilDawn)
drawLabel(object: object, rect: rect, slackX: slackX, offset: -15, time: tides.civilDusk)
}
转到 viewDidLoad 中的主线程并执行操作将解决您的问题
DispatchQueue.main.async {
// here you can add anything it will have proper frame
}
一些观察:
如果您在视图控制器中调整视图的
frame
,正确的位置是viewDidLayoutSubviews
。是的,这被调用了很多次,但它通常不会对性能产生任何可观察到的影响。我不会建议
setNeedsLayout
、layoutIfNeeded
或viewDidLoad
中的任何那些极其脆弱的技术。如果您要在视图控制器中执行此操作,viewDidLayoutSubviews
是正确的位置。通常,如果要自定义子视图绘制和子视图布局,您会在视图的
layoutSubviews
中执行此操作,而不是在任何视图控制器方法中执行此操作。同样,如果你正在做任何手动绘图,你会把它放在
UIView
子类(或任何相关的CALayer
子类)中,而不是视图控制器。更好的是,如果可以的话,最好让自动布局系统为您处理,而不是手动调整框架。如果您发现自己手动调整了
frame
,通常会有更好的模式。FWIW,您可以根据其子视图的大小(或固有大小)定义容器视图的约束(并设置相关视图的内容拥抱和压缩阻力)。我们经常将自动布局视为自上而下的引擎,但它可以双向工作。
如果您向我们展示您的“绘图”代码 and/or 一两个屏幕快照,我们可能会提供更具体的建议。