我有一个自定义UIView子类,我在其中以编程方式添加了几个子视图。我正在使用AutoLayout设置所有布局代码。
当我重写UIView的.zero
方法以尝试获取子视图框架时,就会出现问题,因为它们总是返回layoutSubviews()
作为框架。
但是,如果我转到XCode中的View Hiearchy Debugger,则所有帧都已计算并正确显示。
这是我在layoutSubviews(): <PrologueTextView: 0x7fa50961abc0; frame = (19.75 -19.5; 335.5 168); clipsToBounds = YES; autoresize = RM+BM; layer = <CAShapeLayer: 0x60000022be20>>
layoutSubviews(): <Label: 0x7fa509424c60; baseClass = UILabel; frame = (0 0; 0 0); text = 'This is'; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x60400028d160>>
layoutSubviews(): <Label: 0x7fa509424f60; baseClass = UILabel; frame = (0 0; 0 0); text = 'some sample'; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x60400028d2a0>>
layoutSubviews(): <Label: 0x7fa509425260; baseClass = UILabel; frame = (0 0; 0 0); text = 'text for you'; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x60400028d3e0>>
方法中记录的控制台输出:
internal class PrologueTextView: UIView {
internal var labels: [UILabel] = []
internal let container: UIVisualEffectView = UIVisualEffectView()
// region #Properties
internal var shapeLayer: CAShapeLayer? {
return self.layer as? CAShapeLayer
}
internal override class var layerClass: AnyClass {
return CAShapeLayer.self
}
// endregion
// region #Initializers
internal override init(frame: CGRect) {
super.init(frame: frame)
self.setup()
}
internal required init?(coder: NSCoder) {
super.init(coder: coder)
self.setup()
}
// endregion
// region #UIView lifecycle
internal override func layoutSubviews() {
super.layoutSubviews()
let mask: UIBezierPath = UIBezierPath()
for label in self.labels {
let roundedCorners = self.roundedCorners(for: label)
let maskBezierPath = UIBezierPath(roundedRect: label.frame, byRoundingCorners: roundedCorners, cornerRadius: 4.0)
mask.append(maskBezierPath)
}
self.shapeLayer?.path = mask.cgPath
print("layoutSubviews(): \(self)")
print("layoutSubviews(): \(labels[0])")
print("layoutSubviews(): \(labels[1])")
print("layoutSubviews(): \(labels[2])")
}
// endregion
// region #Helper methods
private func setup() {
self.setupSubviews()
self.setupSubviewsAnchors()
}
private func setupSubviews() {
self.container.effect = UIBlurEffect(style: .light)
self.container.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(self.container)
let someSampleText = "This is\nsome sample\ntext for you"
for paragraph in someSampleText.components(separatedBy: "\n") {
let label = UILabel()
label.text = paragraph
label.translatesAutoresizingMaskIntoConstraints = false
self.labels.append(label)
self.container.contentView.addSubview(label)
}
}
private func setupSubviewsAnchors() {
NSLayoutConstraint.activate([
self.container.topAnchor.constraint(equalTo: self.topAnchor),
self.container.bottomAnchor.constraint(equalTo: self.bottomAnchor),
self.container.leadingAnchor.constraint(equalTo: self.leadingAnchor),
self.container.trailingAnchor.constraint(equalTo: self.trailingAnchor)
])
for (index, label) in self.labels.enumerated() {
let offset = 16.0 * CGFloat(index)
if index == 0 {
label.topAnchor.constraint(equalTo: self.container.contentView.topAnchor).isActive = true
} else {
let prev = self.labels[index - 1]
label.topAnchor.constraint(equalTo: prev.bottomAnchor).isActive = true
if index == self.labels.count - 1 {
label.bottomAnchor.constraint(equalTo: self.container.contentView.bottomAnchor).isActive = true
}
}
NSLayoutConstraint.activate([
label.leadingAnchor.constraint(equalTo: self.container.leadingAnchor, constant: offset),
label.trailingAnchor.constraint(lessThanOrEqualTo: self.container.trailingAnchor)])
}
}
private func roundedCorners(for label: Label) -> UIRectCorner {
switch label {
case self.labels.first:
return [.topLeft, .topRight, .bottomRight]
case self.labels.last:
return [.topRight, .bottomLeft, .bottomRight]
default:
return [.topRight, .bottomLeft]
}
}
// endregion
}
这是我的UIView子类相关代码:
{{1}}
那么,在AutoLayout计算并设置视图及其子视图的框架之后,是否有任何UIView方法会被调用?
答案 0 :(得分:0)
您需要在打印行之前致电self.container.layoutIfNeeded()
internal class PrologueTextView: UIView {
internal var labels: [UILabel] = []
internal let container: UIVisualEffectView = UIVisualEffectView()
// region #Properties
internal var shapeLayer: CAShapeLayer? {
return self.layer as? CAShapeLayer
}
internal override class var layerClass: AnyClass {
return CAShapeLayer.self
}
// endregion
// region #Initializers
internal override init(frame: CGRect) {
super.init(frame: frame)
self.setup()
}
internal required init?(coder: NSCoder) {
super.init(coder: coder)
self.setup()
}
// endregion
// region #UIView lifecycle
internal override func layoutSubviews() {
super.layoutSubviews()
let mask: UIBezierPath = UIBezierPath()
for label in self.labels {
let roundedCorners = self.roundedCorners(for: label)
let maskBezierPath = UIBezierPath(roundedRect: label.frame, byRoundingCorners: roundedCorners, cornerRadii: CGSize(width: 20, height: 20))
mask.append(maskBezierPath)
}
self.shapeLayer?.path = mask.cgPath
self.container.layoutIfNeeded() // here
print("layoutSubviews(): \(self)")
print("layoutSubviews(): \(labels[0])")
print("layoutSubviews(): \(labels[1])")
print("layoutSubviews(): \(labels[2])")
}
// endregion
// region #Helper methods
private func setup() {
self.setupSubviews()
self.setupSubviewsAnchors()
}
private func setupSubviews() {
self.container.effect = UIBlurEffect(style: .light)
self.container.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(self.container)
let someSampleText = "This is\nsome sample\ntext for you"
for paragraph in someSampleText.components(separatedBy: "\n") {
let label = UILabel()
label.text = paragraph
label.translatesAutoresizingMaskIntoConstraints = false
self.labels.append(label)
self.container.contentView.addSubview(label)
}
}
private func setupSubviewsAnchors() {
NSLayoutConstraint.activate([
self.container.topAnchor.constraint(equalTo: self.topAnchor),
self.container.bottomAnchor.constraint(equalTo: self.bottomAnchor),
self.container.leadingAnchor.constraint(equalTo: self.leadingAnchor),
self.container.trailingAnchor.constraint(equalTo: self.trailingAnchor)
])
for (index, label) in self.labels.enumerated() {
let offset = 16.0 * CGFloat(index)
if index == 0 {
label.topAnchor.constraint(equalTo: self.container.contentView.topAnchor).isActive = true
} else {
let prev = self.labels[index - 1]
label.topAnchor.constraint(equalTo: prev.bottomAnchor).isActive = true
if index == self.labels.count - 1 {
label.bottomAnchor.constraint(equalTo: self.container.contentView.bottomAnchor).isActive = true
}
}
NSLayoutConstraint.activate([
label.leadingAnchor.constraint(equalTo: self.container.leadingAnchor, constant: offset),
label.trailingAnchor.constraint(lessThanOrEqualTo: self.container.trailingAnchor)])
}
}
private func roundedCorners(for label: UILabel) -> UIRectCorner {
switch label {
case self.labels.first:
return [.topLeft, .topRight, .bottomRight]
case self.labels.last:
return [.topRight, .bottomLeft, .bottomRight]
default:
return [.topRight, .bottomLeft]
}
}
// endregion
}
答案 1 :(得分:0)
@available(iOS 6.0,*) open func updateConstraints()//对此进行覆盖,以在约束更新过程中调整您的特殊约束
super.updateConstraints()框架之后的尺寸应正确
答案 2 :(得分:0)
与之抗争之后,我弄清楚了发生了什么事。
layoutSubviews()
确实是必经之路;
它将计算视图的框架及其直接子视图。
这里的问题是UILabels不是主UIView子类的子视图,而是主UIView子视图(效果视图)的子视图。
您的视图层次结构示例:
--> TestView
--> EffectView
--> UILabel
--> UILabel
--> UILabel
如您所见,layoutSubviews()
将为TestView
和EffectView
提供正确的帧,因为它是其直接子视图,但不会为您提供{{1 }},因为它们不是UILabels
的直接子视图。