Come faccio a scrivere un init personalizzato per una sottoclasse UIView in Swift?


125

Di 'che voglio inituna UIViewsottoclasse con a Stringe an Int.

Come farei questo in Swift se sto solo subclassando UIView? Se faccio solo una init()funzione personalizzata ma i parametri sono String e Int, mi dice che "super.init () non viene chiamato prima di tornare dall'inizializzatore".

E se chiamo super.init()mi viene detto che devo usare un inizializzatore designato. Cosa dovrei usare lì? La versione del telaio? La versione del programmatore? Tutti e due? Perché?

Risposte:


207

La init(frame:)versione è l'inizializzatore predefinito. È necessario chiamarlo solo dopo aver inizializzato le variabili dell'istanza. Se questa vista viene ricostituita da un pennino, l'inizializzatore personalizzato non verrà chiamato e init?(coder:)verrà invece chiamata la versione. Poiché Swift ora richiede un'implementazione del necessario init?(coder:), ho aggiornato l'esempio seguente e ho cambiato le letdichiarazioni delle variabili in vare facoltativo. In questo caso, li inizializzeresti in awakeFromNib()o in un momento successivo.

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
Quindi sicuramente farli var. Ma la best practice predefinita in Swift è dichiarare le variabili a letmeno che non ci sia un motivo per dichiararle var. Non vi era alcun motivo per farlo nel mio esempio di codice sopra, quindi let.
Wolf McNally,

2
Questo codice non viene compilato. È necessario implementare l'inizializzatore richiesto init(coder:).
Decade Moon,

3
interessante come questo compilato anni fa. Oggi si lamenta sotto init (coder :) che "Proprietà self.s non inizializzata alla chiamata super.init"
mafiOSo

Esempio fisso per Swift 3.1. Compilare in un parco giochi importando UIKit.
Wolf McNally,

1
@LightNight Ho creato se ifacoltativo per semplificare le cose qui. Se non fossero facoltativi, dovrebbero anche essere inizializzati nell'inizializzatore richiesto. Renderli facoltativi significa che arrivano nilquando super.init()viene chiamato. Se non fossero facoltativi, avrebbero effettivamente bisogno di essere assegnati prima di chiamare super.init ().
Wolf McNally,

32

Creo un init comune per il designato e richiesto. Per comodità, delegare init(frame:)con frame zero.

Avere frame zero non è un problema perché in genere la vista è all'interno di una vista di ViewController; la tua vista personalizzata avrà una buona e sicura possibilità di impaginare le sue sottoview quando la sua superview chiama layoutSubviews()o updateConstraints(). Queste due funzioni sono chiamate dal sistema in modo ricorsivo in tutta la gerarchia della vista. Puoi usare updateContstraints()o layoutSubviews(). updateContstraints()viene chiamato per primo, quindi layoutSubviews(). In updateConstraints()assicuratevi di chiamare super- ultima . In layoutSubviews(), chiama prima prima .

Ecco cosa faccio:

@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
Non dovrebbe funzionare: l'uso di 'self' nel metodo chiama 'commonInit' prima che super.init inizializzi self
surfrider l'

1
Inizializza argomenti personalizzati dopo la chiamata self.init. Aggiornato la mia risposta.
MH175,

1
Ma cosa succede se si desidera inizializzare alcune proprietà nel commonInitmetodo, ma non è possibile posizionarle dopo superin questo caso perché è necessario inizializzare tutte le proprietà PRIMA di superchiamare. Lol sembra un circuito morto.
surfrider,

1
Ecco come spesso funziona l'inizializzazione di Swift: cerca "inizializzazione in due fasi". Puoi usare opzionali implicitamente da scartare, ma ti sconsiglio. La tua architettura, specialmente quando si tratta di viste, dovrebbe inizializzare tutte le proprietà locali. Ho usato questo metodo commonInit () per centinaia di visualizzazioni ora. Funziona
MH175,

17

Ecco come lo faccio su iOS 9 in 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"); }
}

Ecco un progetto completo con l'esempio:


2
questo userebbe l'intero display per View
RaptoX

1
Sì! Se sei interessato a una subview parziale, fammi sapere e pubblicherò anche questo
J-Dizzle

1
Mi piace questa risposta al meglio, perché avere fatalError significa che non devo inserire alcun codice nell'init richiesto.
Carter Medlin,

1
@ J-Dizzle, mi piacerebbe vedere la soluzione per viste parziali.
Ari Lacenski,

la tua risposta non dice il contrario della risposta accettata? Voglio dire che stai facendo la personalizzazione dopo super.init, ma ha detto che dovrebbe essere fatto prima super.init...
Honey

11

Ecco come faccio una sottoview su iOS in 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
Buon uso della chiamata fatalError (). Dovevo usare gli opzionali solo per mettere a tacere gli avvertimenti di un inizializzatore che non veniva nemmeno usato. Questo lo ha chiuso! Grazie.
Mike Critchley,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.