Exemple de sous-classe UIView personnalisée
Je crée généralement des applications iOS sans utiliser de storyboards ou de pointes. Je vais partager quelques techniques que j'ai apprises pour répondre à vos questions.
Masquer les init
méthodes indésirables
Ma première suggestion est de déclarer une base UIView
pour masquer les initialiseurs indésirables. J'ai discuté de cette approche en détail dans ma réponse à "Comment masquer les initialiseurs spécifiques au storyboard et à la pointe dans les sous-classes de l'interface utilisateur" . Remarque: cette approche suppose que vous n'utiliserez pas BaseView
ou ses descendants dans les storyboards ou les nibs, car cela provoquera intentionnellement le blocage de l'application.
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")
}
}
Votre sous-classe UIView personnalisée doit hériter de BaseView
. Il doit appeler super.init () dans son initialiseur. Il n'a pas besoin d'être mis en œuvre init(coder:)
. Ceci est démontré dans l'exemple ci-dessous.
Ajout d'un UITextField
Je crée des propriétés stockées pour les sous-vues référencées en dehors de la init
méthode. Je le ferais généralement pour un UITextField. Je préfère subviews de instancier au sein de la déclaration de la propriété sous - vue comme ceci: let textField = UITextField()
.
UITextField ne sera visible que si vous l'ajoutez à la liste de sous-vues de la vue personnalisée en appelant addSubview(_:)
. Ceci est démontré dans l'exemple ci-dessous.
Disposition programmatique sans disposition automatique
UITextField ne sera visible que si vous définissez sa taille et sa position. Je fais souvent la mise en page dans le code (sans utiliser la mise en page automatique) dans la méthode layoutSubviews . layoutSubviews()
est appelé initialement et chaque fois qu'un événement de redimensionnement se produit. Cela permet d'ajuster la disposition en fonction de la taille de CustomView. Par exemple, si CustomView apparaît sur toute la largeur sur différentes tailles d'iPhones et d'iPad et s'ajuste pour la rotation, il doit s'adapter à de nombreuses tailles initiales et se redimensionner dynamiquement.
Vous pouvez vous référer à frame.height
et à l' frame.width
intérieur layoutSubviews()
pour obtenir les dimensions de CustomView à titre de référence. Ceci est démontré dans l'exemple ci-dessous.
Exemple de sous-classe UIView
Une sous-classe UIView personnalisée contenant un UITextField qui n'a pas besoin d'être implémenté 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)
}
}
Disposition programmatique avec disposition automatique
Vous pouvez également implémenter la disposition à l'aide de la disposition automatique dans le code. Comme je ne le fais pas souvent, je ne montrerai pas d'exemple. Vous pouvez trouver des exemples de mise en œuvre de la disposition automatique dans le code sur Stack Overflow et ailleurs sur Internet.
Cadres de mise en page programmatiques
Il existe des frameworks open source qui implémentent la mise en page dans le code. Un qui m'intéresse mais que je n'ai pas essayé est LayoutKit . Il a été rédigé par l'équipe de développement sur LinkedIn. Depuis le référentiel Github: "LinkedIn a créé LayoutKit car nous avons constaté que la mise en page automatique n'est pas assez performante pour les hiérarchies de vues complexes dans les vues déroulantes."
Pourquoi mettre fatalError
eninit(coder:)
Lors de la création de sous-classes UIView qui ne seront jamais utilisées dans un storyboard ou une nib, vous pouvez introduire des initialiseurs avec des paramètres et des exigences d'initialisation différents qui n'ont pas pu être appelés par la init(coder:)
méthode. Si vous n'avez pas échoué à init (coder :) avec a fatalError
, cela pourrait entraîner des problèmes très déroutants sur toute la ligne en cas d'utilisation accidentelle dans un storyboard / nib. FatalError affirme ces intentions.
required init?(coder aDecoder: NSCoder) {
fatalError("NSCoding not supported")
}
Si vous souhaitez exécuter du code lorsque la sous-classe est créée, qu'elle soit créée dans le code ou dans un storyboard / nib, vous pouvez faire quelque chose comme ce qui suit (basé sur la réponse de 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
}
}