如何仅在首次初始化自定义 UIView 时在 LayoutSubviews 中执行代码

How to execute code in LayoutSubviews only when first initialising a custom UIView

提问人:shbedev 提问时间:8/28/2019 更新时间:9/28/2021 访问量:2604

问:

我们有一个自定义 UIView,它在 Interface Builder 中使用约束,并且我们修改了此视图以具有我们需要绘制的自定义形状。

形状大小取决于在 Interface Builder 中应用于此自定义视图的约束,因此为了在自定义 UIView 中以正确的大小获取形状,我们需要在 LayoutSubviews 中调用它。

问题是,当在屏幕方向期间调用 LayoutSubviews 时,会将另一个形状添加到屏幕中,因为它是在自定义 UIView 的设置函数中调用的。

可以通过添加一个布尔值来解决此行为,该布尔值确定这是否是第一次调用 LayoutSubviews,并且仅当它是时,然后将形状层添加到视图中。

我在这里和网络上做了一些研究,找到了一篇关于如何用不同的方法实现所描述的行为的文章。

https://thomasclowes.com/ios-when-to-layout-your-views/

作者提到了一个自定义类 BaseView 和一个可以实现的协议来解决这种行为。

该解决方案编写在“UIView 和 layoutSubviews”下。

我正在努力理解解决方案,并想知道是否有人可以澄清并展示有关如何实现本文中提到的解决方案的代码示例。

提前致谢。

class CustomView: UIView {

    private var firstTimeInit = true

    override func layoutSubviews() {
        super.layoutSubviews()

        setup()
    }

    private func setup() {

        // Check if the the method was already executed
        if firstTimeInit == false { return }

        let label = UILabel(frame: CGRect(x: 0, y: 0, width: 0, height: 21))
        label.font = UIFont.systemFont(ofSize: 13, weight: .bold)
        label.text = "Daily"
        label.textAlignment = .center
        label.sizeToFit()
        addSubview(label)
        label.center.x = bounds.midX

        let shapePath = UIBezierPath()
        shapePath.move(to: CGPoint(x: 0, y: 0))
        shapePath.addLine(to: CGPoint(x: frame.width, y: 0))

        let shapeLayer = CAShapeLayer()
        shapeLayer.path = shapePath.cgPath
        shapeLayer.strokeColor = UIColor(hex: 0xD5E5EF).cgColor
        shapeLayer.lineWidth = 3
        shapeLayer.lineCap = .round
        shapeLayer.position.y = frame.height / 2
        shapeLayer.lineDashPattern = [frame.width / 2.5, frame.width / 5, frame.width / 2.5] as [NSNumber]
        layer.addSublayer(shapeLayer)

        firstTimeInit = false
    }
}
iOS Swift iPhone UI 布局子视图

评论


答:

3赞 Mateusz Chechliński 8/28/2019 #1

你为什么不创建你的 and once,将其添加到构造函数中的子视图,然后只用于重新计算它们的框架或模式?UILabelCAShapeLayerlayoutSubviews

喜欢这个:

class CustomView: UIView {

    private let label = UILabel(frame: CGRect(x: 0, y: 0, width: 0, height: 21))
    private let shapeLayer = CAShapeLayer()

    override init(frame: CGRect) {
        super.init(frame: frame)

        label.font = UIFont.systemFont(ofSize: 13, weight: .bold)
        label.text = "Daily"
        label.textAlignment = .center
        addSubview(label)

        shapeLayer.strokeColor = UIColor(hex: 0xD5E5EF).cgColor
        shapeLayer.lineWidth = 3
        shapeLayer.lineCap = .round
        layer.addSublayer(shapeLayer)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func layoutSubviews() {
        super.layoutSubviews()

        label.sizeToFit()
        label.center.x = bounds.midX

        let shapePath = UIBezierPath()
        shapePath.move(to: CGPoint(x: 0, y: 0))
        shapePath.addLine(to: CGPoint(x: frame.width, y: 0))

        shapeLayer.path = shapePath.cgPath
        shapeLayer.position.y = frame.height / 2
        shapeLayer.lineDashPattern = [frame.width / 2.5, frame.width / 5, frame.width / 2.5] as [NSNumber]
    }
}

这样,您的自定义形状将适应变化,而不会自我复制。frame

显然,如果你使用它,你可能需要实际实现。required init?(coder aDecoder: NSCoder)

评论

0赞 shbedev 8/28/2019
这可能是一个很好的解决方案,但据我所知,viewDidLoad 是 UIViewController 的一个函数,因此它在 UIView 中不可用。我想将视图设置与视图控制器分开,因此我想在自定义 UIView 类中进行设置。谢谢:)
0赞 Mateusz Chechliński 8/28/2019
@Sam *facepalm* 我确信我说的是 UIViewController。请看编辑后的答案
0赞 chirag90 8/28/2019 #2

一种方法是在自定义视图中使用 didSet 方法。例如

class CustomView: UIView {

    var firstTimeInit : Bool = false {
        didSet {
            layoutSubviews()
        }
    }


    override func layoutSubviews() {
        super.layoutSubviews()
        // i am only changing the color to check if the layoutSubview is called. 
        // i have a viewController in storyboard with a UIView Background Color set to gray. 
        backgroundColor = UIColor.red
    }

}

若要使用,请确保在自定义视图中调用 didSet 方法,可以在要加载自定义视图的视图控制器的 viewdidLoad 函数中使用它。

class ViewController: UIViewController {

    let customView = CustomView()

    override func viewDidLoad() {
        super.viewDidLoad()
        customView.viewDidSet = true
    }
}