ios:关于init(frame :)和init的问题?(编码器:)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ios:关于init(frame :)和init的问题?(编码器:)相关的知识,希望对你有一定的参考价值。

Apple的教程描述了init(frame:)init?(coder:)之间的区别

您通常使用以下两种方法之一创建视图:通过以编程方式初始化视图,或者允许视图由故事板加载。每种方法都有一个相应的初始化程序:init(frame:)用于以编程方式初始化视图,init?(coder:)用于从故事板加载视图。您需要在自定义控件中实现这两种方法。在设计应用程序时,Interface Builder在将视图添加到画布时以编程方式实例化视图。在运行时,您的应用程序会从故事板中加载视图。

我对“程序初始化”和“由故事板加载”的描述感到困惑。假设我有一个名为UIViewMyView的子类,“程序初始化”意味着我编写代码来将MyView的实例添加到某个地方,例如:

override func viewDidLoad() {      
        super.viewDidLoad()
        let myView = MyView()  // init(frame:) get invoked here??
}

init?(coder:)Main.storyboard被调用时,我从对象库中拖出一个UIView然后在身份检查器中我将它的类设置为MyView

此外,在我的xcode项目中,这两种方法最终得到了不同的模拟器和Main.storyboard布局,代码相同:enter image description here

import UIKit

@IBDesignable
class RecordView: UIView {

    @IBInspectable
    var borderColor: UIColor = UIColor.clear {
        didSet {
            self.layer.borderColor = borderColor.cgColor
        }
    }

    @IBInspectable
    var borderWidth: CGFloat = 20 {
        didSet {
            layer.borderWidth = borderWidth
        }
    }

    @IBInspectable
    var cornerRadius: CGFloat = 100 {
        didSet {
            layer.cornerRadius = cornerRadius
        }
    }

    private var fillView = UIView()

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setupFillView()
    }

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



    private func setupFillView() {
        let radius = (self.cornerRadius - self.borderWidth) * 0.95
        fillView.frame = CGRect(origin: CGPoint.zero, size: CGSize(width: radius * 2, height: radius * 2))
        fillView.center = CGPoint(x: self.bounds.midX, y: self.bounds.midY)
        fillView.layer.cornerRadius = radius
        fillView.backgroundColor = UIColor.red
        self.addSubview(fillView)
    }

    override func layoutSubviews() {
        super.layoutSubviews()
    }

    func didClick() {
        UIView.animate(withDuration: 1.0, animations: {
            self.fillView.transform = CGAffineTransform(scaleX: 0.6, y: 0.6)
        }) { (true) in
            print()
        }
    }
}

他们为什么表现不同? (我从对象库拖动一个UIView并将其类设置为RecordView)

答案

首先,你在init?(coder:)init(frame:)之间的划分基本上是正确的。当您实际运行应用程序时实例化故事板场景时使用前者,但是当您使用let foo = RecordView()let bar = RecordView(frame: ...)以编程方式对其进行实例化时,将使用后者。此外,在IB中预览init(frame:)视图时使用@IBDesignable

第二,关于你的问题,我建议你从center删除fillViewsetupFillView(以及角半径的东西)的设置。问题是,当调用init时,你通常不知道bounds最终会是什么。你应该在center中设置layoutSubviews,每次视图改变大小时都会调用它。

class RecordView: UIView {  // this is the black circle with a white border

    private var fillView = UIView() // this is the inner red circle

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setupFillView()
    }

    override init(frame: CGRect = .zero) {
        super.init(frame: frame)
        setupFillView()
    }

    private func setupFillView() {
        fillView.backgroundColor = .red
        self.addSubview(fillView)
    }

    override func layoutSubviews() {
        super.layoutSubviews()

        let radius = (cornerRadius - borderWidth) * 0.95       // these are not defined in this snippet, but I simply assume you omitted them for the sake of brevity?
        fillView.frame = CGRect(origin: .zero, size: CGSize(width: radius * 2, height: radius * 2))
        fillView.layer.cornerRadius = radius
        fillView.center = CGPoint(x: bounds.midX, y: bounds.midY)
    }
}
另一答案

我对编程“以编程方式初始化”和“由故事板加载”的描述感到困惑。

基于对象的编程是关于类和实例的。您需要创建一个类的实例。使用Xcode,有两种截然不同的方法来获取类的实例:

  • 您的代码创建实例
  • 你加载一个笔尖(在故事板中这样的视图控制器的视图)和nib加载过程创建实例并交给你

在这两种情况下调用的初始化程序是不同的。如果您的代码创建了一个UIView实例,那么您必须调用的指定初始化程序是init(frame:)。但是如果nib创建视图,则nib加载进程调用的指定初始化程序是init(coder:)

因此,如果您有一个UIView子类并且想要覆盖初始化程序,则必须考虑将调用哪个初始化程序(基于如何创建视图实例)。

以上是关于ios:关于init(frame :)和init的问题?(编码器:)的主要内容,如果未能解决你的问题,请参考以下文章

iOS 中怎么修改 UIImageView的frame

为啥在我的自定义 UIView 类中没有调用 init(frame: CGRect)?

动画起源 iOS

@IBDesignable UIView 不在情节提要中调用 init

iOS四大控件

iOS:关于UIView切角的两种实现方式