Jak napisać niestandardowy plik init dla podklasy UIView w języku Swift?


125

Powiedzmy chcę initna UIViewpodklasy z Stringi Int.

Jak mam to zrobić w Swift, skoro tylko podklasuję UIView? Jeśli po prostu init()utworzę funkcję niestandardową, ale parametry to String i Int, to mówi mi, że „super.init () nie jest wywoływana przed powrotem z inicjatora”.

A jeśli zadzwonię super.init(), powiedziano mi, że muszę użyć wyznaczonego inicjatora. Czego mam tam używać? Wersja ramowa? Wersja kodera? Obie? Czemu?

Odpowiedzi:


207

init(frame:)Wersja jest domyślnym inicjator. Musisz go wywołać dopiero po zainicjowaniu zmiennych instancji. Jeśli ten widok jest odtwarzany ze stalówki, twój niestandardowy inicjator nie zostanie wywołany, a zamiast tego init?(coder:)zostanie wywołana wersja. Ponieważ Swift wymaga teraz implementacji wymaganego init?(coder:), zaktualizowałem poniższy przykład i zmieniłem letdeklaracje zmiennych na vari opcjonalne. W takim przypadku zainicjowałbyś je awakeFromNib()później lub później.

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
Więc jak najbardziej je zrób var. Ale domyślną najlepszą praktyką w Swift jest deklarowanie zmiennych, letchyba że istnieje powód, aby je zadeklarować var. W związku z tym w powyższym przykładzie kodu nie było takiego powodu let.
— Wolf McNally

2
Ten kod nie kompiluje się. Musisz zaimplementować wymagany inicjator init(coder:).
— Decade Moon

3
ciekawe, jak to skompilowano lata temu. Obecnie narzeka init (koder :), że "Property self.s nie jest zainicjowany w wywołaniu super.init"
— mafiOSo

Naprawiono przykład dla Swift 3.1. Kompiluje się w ramach placu zabaw importującego UIKit.
— Wolf McNally

1
@LightNight stworzyłem si iopcjonalnie, aby wszystko było proste. Gdyby nie były opcjonalne, musiałyby również zostać zainicjowane w wymaganym inicjatorze. Uczynienie ich opcjonalnymi oznacza, że ​​mogą być, nilgdy super.init()zostanie wywołany. Gdyby nie były opcjonalne, rzeczywiście należałoby je przypisać przed wywołaniem super.init ().
— Wolf McNally

32

Tworzę wspólny init dla wyznaczonych i wymaganych. Dla wygody deleguję inity init(frame:)z ramką zerową.

Brak ramki nie jest problemem, ponieważ zazwyczaj widok znajduje się wewnątrz widoku ViewControllera; Twój widok niestandardowy będzie miał dobrą, bezpieczną szansę na ułożenie swoich podglądów podrzędnych, gdy wywoła nadzór layoutSubviews()lub updateConstraints(). Te dwie funkcje są wywoływane przez system rekurencyjnie w całej hierarchii widoków. Możesz użyć albo updateContstraints()lub layoutSubviews(). updateContstraints()jest wtedy wywoływana jako pierwsza layoutSubviews(). W updateConstraints()upewnij się, aby zadzwonić Super ostatni . W layoutSubviews()zadzwoń Super pierwszy .

Oto co robię:

@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
To nie powinno działać: użycie `` self '' w wywołaniu metody `` commonInit '', zanim super.init zainicjuje self
— surfrider

1
Zainicjuj argumenty niestandardowe po wywołaniu self.init. Zaktualizowałem moją odpowiedź.
— MH175

1
Ale co, jeśli chcesz zainicjalizować niektóre właściwości w commonInitmetodzie, ale nie możesz ich umieścić później, superponieważ powinieneś zainicjować wszystkie właściwości PRZED superwywołaniem. Lol, wygląda na martwą pętlę.
— surfrider

1
Oto jak często działa inicjalizacja Swift: wyszukaj „inicjalizacja dwufazowa”. Możesz używać niejawnie rozpakowanych opcji, ale odradzam to. Twoja architektura, szczególnie w przypadku widoków, powinna inicjalizować wszystkie lokalne właściwości. Użyłem tej metody commonInit () do setek widoków. To działa
— MH175

17

Oto jak to robię na iOS 9 w 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"); }
}

Oto pełny projekt z przykładem:


2
wykorzystałoby to cały wyświetlacz dla View
— RaptoX

1
tak! Jeśli interesuje Cię częściowy podgląd, daj mi znać, a ja też to opublikuję
— J-Dizzle

1
Najbardziej podoba mi się ta odpowiedź, ponieważ posiadanie fatalError oznacza, że ​​nie muszę umieszczać żadnego kodu w wymaganym pliku init.
— Carter Medlin

1
@ J-Dizzle, chciałbym zobaczyć rozwiązanie dla widoków częściowych.
— Ari Lacenski

czy twoja odpowiedź nie jest przeciwieństwem zaakceptowanej odpowiedzi? Mam na myśli, że robisz dostosowywanie później super.init, ale powiedział, że powinno to być zrobione przed super.init...
— Kochanie,

11

Oto, jak mogę zrobić podgląd podrzędny na iOS w 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
Niezłe użycie wywołania fatalError (). Musiałem używać opcji tylko po to, aby wyciszyć ostrzeżenia z inicjatora, który nawet nie był używany. To zamknęło się! Dzięki.
— Mike Critchley
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.