Как написать пользовательский init для подкласса UIView в Swift?

Скажем, я хочу подкласс init a UIView с String и Int.

Как бы это сделать в Swift, если я просто подклассифицирую UIView? Если я просто создаю пользовательскую функцию init(), но параметры - это String и Int, она сообщает мне, что "super.init() не вызывается перед возвратом из инициализатора".

И если я позвоню super.init(), мне сказали, что я должен использовать назначенный инициализатор. Что я должен там использовать? Кадровая версия? Версия кодера? И то и другое? Почему?

Ответ 1

Версия init(frame:) является инициализатором по умолчанию. Вы должны вызывать его только после инициализации ваших переменных экземпляра. Если это представление восстанавливается из Nib, тогда ваш пользовательский инициализатор не будет вызываться, а вместо него будет вызываться init?(coder:). Поскольку Swift теперь требует реализации требуемого init?(coder:), я обновил приведенный ниже пример и изменил объявления переменных let на var и необязательно. В этом случае вы должны инициализировать их в awakeFromNib() или через некоторое время.

class TestView : UIView {
    var s: String?
    var i: Int?
    init(s: String, i: Int) {
        self.s = s
        self.i = i
        super.init(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
    }

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

Ответ 2

Вот как я это делаю на iOS 9 в Swift -

import UIKit

class CustomView : UIView {

    init() {
        super.init(frame: UIScreen.mainScreen().bounds);

        //for debug validation
        self.backgroundColor = UIColor.blueColor();
        print("My Custom Init");

        return;
    }

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

Вот полный проект с примером:

Ответ 3

Вот как я делаю Subview на iOS в Swift -

class CustomSubview : UIView {

    init() {
        super.init(frame: UIScreen.mainScreen().bounds);

        let windowHeight : CGFloat = 150;
        let windowWidth  : CGFloat = 360;

        self.backgroundColor = UIColor.whiteColor();
        self.frame = CGRectMake(0, 0, windowWidth, windowHeight);
        self.center = CGPoint(x: UIScreen.mainScreen().bounds.width/2, y: 375);

        //for debug validation
        self.backgroundColor = UIColor.grayColor();
        print("My Custom Init");

        return;
    }

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

Ответ 4

Я создаю общий init для назначенного и требуемого. Для удобства я передаю init(frame:) с нулевым кадром.

Наличие нулевого кадра не является проблемой, потому что обычно представление находится внутри представления ViewController. Супервизор вызывает layoutSubviews() или updateConstraints(), который рекурсивно вызывается системой в иерархии представлений.

Если у вашего пользовательского представления есть subviews, вы можете установить рамки subviews либо в updateContstraints(), либо layoutSubviews(). updateContstraints() вызывается первым, тогда layoutSubviews(). В updateConstraints() обязательно вызовите super last. В layoutSubviews() вызовите super first.

Вот что я делаю:

class MyView: UIView {

      convenience init(args: Whatever) {
          self.init(frame: CGRect.zero)
          //assign custom vars
      }

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

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

      private func commonInit() {
           //custom initialization
      }

      override func updateConstraints() {
           //set subview constraints here
           super.updateConstraints()
      }

      override func layoutSubviews() {
           super.layoutSubviews()
           //manually set subview frames here
      }

}