Як написати власну init для підкласу UIView у Swift?


125

Скажімо , я хочу initв UIViewпідклас з Stringі Int.

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

І якщо я телефоную, super.init()мені кажуть, що я повинен використовувати призначений ініціалізатор. Що я маю там використовувати? Версія кадру? Версія кодера? Обидва? Чому?

Відповіді:


207

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

5
Тоді всіма силами їх складіть var. Але найкраща практика за замовчуванням у Swift полягає в оголошенні змінних, letякщо немає підстав їх оголошувати var. Тому в моєму прикладі коду не було такої причини, отже let.
Вовк МакНаллі

2
Цей код не компілюється. Вам потрібно здійснити необхідний ініціалізатор init(coder:).
Місяць десятиліття

3
Цікаво, як це складено років тому. Сьогодні він скаржиться під init (coder :), що "Власність self.s не ініціалізується при виклику super.init"
mafiOSo

Виправлений приклад для Swift 3.1. Збирається під майданчик для імпорту UIKit.
Wolf McNally

1
@LightNight Я зробив sі iнеобов’язково, щоб тут було просто. Якщо вони не були необов'язковими, їх також потрібно було б ініціалізувати у необхідному ініціалізаторі. Зробити їх необов'язковими засобами, якими вони можуть стати, nilколи super.init()їх викликають. Якби вони не були необов’язковими, їх дійсно потрібно було б призначити перед викликом super.init ().
Wolf McNally

33

Я створюю загальну ініціативу для призначених і потрібних. Для зручності inits я делегую init(frame:)з рамкою нуля.

Мати нульовий кадр не є проблемою, тому що, як правило, подання знаходиться всередині подання ViewController; ваш користувальницький вигляд отримає хороший, безпечний шанс розташувати свої підгляди, коли його буде переглянуто layoutSubviews()або updateConstraints(). Ці дві функції система викликає рекурсивно у всій ієрархії перегляду. Ви можете використовувати updateContstraints()або layoutSubviews(). updateContstraints()називається спочатку, потім layoutSubviews(). У updateConstraints()Обов'язково покличте супер останній . У layoutSubviews(), зателефонуйте супер першим .

Ось що я роблю:

@IBDesignable
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()
      }

      override func prepareForInterfaceBuilder() {
           super.prepareForInterfaceBuilder()
           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
      }

}

1
Це не повинно працювати: Використання "self" у виклику методу "commonInit" перед тим, як super.init ініціалізує self
surfrider

1
Ініціалізуйте спеціальні аргументи після виклику self.init. Оновлено мою відповідь.
MH175

1
Але що робити, якщо ви хочете ініціалізувати деякі властивості в commonInitметоді, але ви не можете розмістити його superв цьому випадку, тому що ви повинні ініціалізувати всі властивості перед superвикликом. Lol це здається мертвою петлею.
серфрідер

1
Ось як часто працює ініціалізація Swift: пошук "двофазної ініціалізації". Ви можете використовувати неявно розгорнуті опціони, але я рекомендую проти цього. Ваша архітектура, особливо коли стосується поглядів, повинна ініціалізувати всі локальні властивості. Зараз я використовував цей метод commonInit () для сотень переглядів. Це працює
MH175

17

Ось як я це роблю на 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"); }
}

Ось повний проект із прикладом:


2
це використовує весь дисплей для View
RaptoX

1
так! Якщо вас зацікавив частковий підвід, дайте мені знати, і я також опублікую це
J-Dizzle

1
Мені подобається ця відповідь найкраще, тому що мати fatalError означає, що мені не потрібно вводити жоден код у потрібний init.
Картер Медлін

1
@ J-Dizzle, я хотів би побачити рішення для часткових поглядів.
Арі Лаченскі

Чи не у вашій відповіді сказано протилежне прийнятій відповіді? Я маю на увазі, що ви робите налаштування після super.init, але він сказав, що це потрібно зробити раніше super.init...
Мед

11

Ось як я роблю підзор на 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
Гарне використання дзвінка fatalError (). Мені довелося використовувати необов’язкові лише для того, щоб заглушити попередження від ініціалізатора, який навіть не використовувався. Це заткнись! Дякую.
Майк Крічлі
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.