代码之家  ›  专栏  ›  技术社区  ›  Tylerian

如何从具有自动布局的uiview子类中获取帧

  •  1
  • Tylerian  · 技术社区  · 6 年前

    我有一个自定义的uiview子类,我在其中以编程方式添加了几个子视图。我正在用自动布局设置所有布局代码。

    当我重写uiview的 layoutSubviews() 方法尝试获取子视图帧,因为它们总是返回 .zero 作为他们的框架。

    但是,如果我转到Xcode中的View Hierarchy调试器,所有帧都会正确计算和显示。

    这里是控制台输出,我记录在 布局子视图() 方法:

    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>>
    

    下面是我的uiview子类相关代码:

    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方法被调用?

    3 回复  |  直到 6 年前
        1
  •  0
  •   Shehata Gamal    6 年前

    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
    }
    
        2
  •  0
  •   whoover    6 年前

    open func updateConstraints()//重写此项以在约束更新过程中调整特殊约束

        3
  •  0
  •   Tylerian    6 年前

    在和它搏斗之后,我已经知道发生了什么。

    layoutSubviews()

    --> TestView
        --> EffectView
            --> UILabel
            --> UILabel
            --> UILabel
    

    TestView EffectView UILabels 测试视图