Esempio di sottoclasse UIView personalizzata
Di solito creo app iOS senza usare storyboard o pennini. Condividerò alcune tecniche che ho imparato per rispondere alle tue domande.
Nascondere init
metodi indesiderati
Il mio primo suggerimento è di dichiarare una base UIView
per nascondere gli inizializzatori indesiderati. Ho discusso questo approccio in dettaglio nella mia risposta a "Come nascondere gli inizializzatori specifici dello storyboard e del pennino nelle sottoclassi dell'interfaccia utente" . Nota: questo approccio presuppone che non utilizzerai i BaseView
suoi discendenti negli storyboard o nei pennini poiché causerà intenzionalmente l'arresto anomalo dell'app.
class BaseView: UIView {
// This initializer hides init(frame:) from subclasses
init() {
super.init(frame: CGRect.zero)
}
// This attribute hides `init(coder:)` from subclasses
@available(*, unavailable)
required init?(coder aDecoder: NSCoder) {
fatalError("NSCoding not supported")
}
}
La tua sottoclasse UIView personalizzata dovrebbe ereditare da BaseView
. Deve chiamare super.init () nel suo inizializzatore. Non è necessario implementarlo init(coder:)
. Ciò è dimostrato nell'esempio seguente.
Aggiunta di un UITextField
Creo proprietà memorizzate per le sottoview referenziate al di fuori del init
metodo. In genere lo farei per un UITextField. Preferisco istanziare le sottoview all'interno della dichiarazione della proprietà della sottoview in questo modo:let textField = UITextField()
.
UITextField non sarà visibile a meno che non lo si aggiunga all'elenco delle sottoview della visualizzazione personalizzata chiamando addSubview(_:)
. Ciò è dimostrato nell'esempio seguente.
Layout programmatico senza layout automatico
UITextField non sarà visibile a meno che non ne imposti le dimensioni e la posizione. Spesso eseguo il layout nel codice (senza utilizzare il layout automatico) all'interno del metodo layoutSubviews .layoutSubviews()
viene chiamato inizialmente e ogni volta che si verifica un evento di ridimensionamento. Ciò consente di regolare il layout in base alle dimensioni di CustomView. Ad esempio, se CustomView appare a tutta larghezza su varie dimensioni di iPhone e iPad e si regola per la rotazione, deve adattarsi a molte dimensioni iniziali e ridimensionare dinamicamente.
È possibile fare riferimento a frame.height
e frame.width
all'interno layoutSubviews()
per ottenere le dimensioni di CustomView come riferimento. Ciò è dimostrato nell'esempio seguente.
Esempio di sottoclasse UIView
Una sottoclasse UIView personalizzata contenente un UITextField che non deve essere implementato init?(coder:)
.
class CustomView: BaseView {
let textField = UITextField()
override init() {
super.init()
// configure and add textField as subview
textField.placeholder = "placeholder text"
textField.font = UIFont.systemFont(ofSize: 12)
addSubview(textField)
}
override func layoutSubviews() {
super.layoutSubviews()
// Set textField size and position
textField.frame.size = CGSize(width: frame.width - 20, height: 30)
textField.frame.origin = CGPoint(x: 10, y: 10)
}
}
Layout programmatico con layout automatico
È inoltre possibile implementare il layout utilizzando Layout automatico nel codice. Dato che non lo faccio spesso, non mostrerò un esempio. Puoi trovare esempi di implementazione del layout automatico nel codice su Stack Overflow e altrove su Internet.
Framework di layout programmatico
Esistono framework open source che implementano il layout nel codice. Uno che mi interessa ma che non ho provato è LayoutKit . È stato scritto dal team di sviluppo e LinkedIn. Dal repository Github: "LinkedIn ha creato LayoutKit perché abbiamo scoperto che il layout automatico non è abbastanza performante per complicate gerarchie di visualizzazione in viste scorrevoli."
Perché mettere fatalError
ininit(coder:)
Quando si creano sottoclassi UIView che non verranno mai utilizzate in uno storyboard o in un pennino, è possibile introdurre inizializzatori con parametri e requisiti di inizializzazione diversi che non possono essere chiamati dal init(coder:)
metodo. Se non hai fallito init (coder :) con un fatalError
, potrebbe portare a problemi molto confusi su tutta la linea se usato accidentalmente in uno storyboard / pennino. Il fatalError afferma queste intenzioni.
required init?(coder aDecoder: NSCoder) {
fatalError("NSCoding not supported")
}
Se si desidera eseguire del codice quando viene creata la sottoclasse, indipendentemente dal fatto che sia stata creata nel codice o in uno storyboard / pennino, è possibile fare qualcosa di simile (basato su risposta di Jeff Gu Kang )
class CustomView: UIView {
override init (frame: CGRect) {
super.init(frame: frame)
initCommon()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
initCommon()
}
func initCommon() {
// Your custom initialization code
}
}