Solution originale
- J'ai créé un XIB et une classe nommée SomeView (utilisé le même nom pour plus de commodité et de lisibilité). J'ai basé les deux sur un UIView.
- Dans le XIB, j'ai changé la classe "File's Owner" en SomeView (dans l'inspecteur d'identité).
- J'ai créé une prise UIView dans SomeView.swift, en la reliant à la vue de niveau supérieur dans le fichier XIB (nommée "vue" pour plus de commodité). J'ai ensuite ajouté d'autres prises à d'autres contrôles dans le fichier XIB au besoin.
- dans SomeView.swift, j'ai chargé le XIB dans l'initialiseur "init with code". Il n'est pas nécessaire d'attribuer quoi que ce soit à «soi». Dès que le XIB est chargé, toutes les prises sont connectées, y compris la vue de niveau supérieur. La seule chose qui manque, c'est d'ajouter la vue de dessus à la hiérarchie des vues:
.
class SomeView: UIView {
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
NSBundle.mainBundle().loadNibNamed("SomeView", owner: self, options: nil)
self.addSubview(self.view); // adding the top level view to the view hierarchy
}
...
}
Notez que de cette façon, j'obtiens une classe qui se charge à partir de nib. Je pourrais alors utiliser SomeView comme classe chaque fois que UIView pourrait être utilisé dans le projet (dans le générateur d'interface ou par programme).
Mise à jour - utilisation de la syntaxe Swift 3
Le chargement d'un xib dans l'extension suivante est écrit comme une méthode d'instance, qui peut ensuite être utilisée par un initialiseur comme celui ci-dessus:
extension UIView {
@discardableResult // 1
func fromNib<T : UIView>() -> T? { // 2
guard let contentView = Bundle(for: type(of: self)).loadNibNamed(String(describing: type(of: self)), owner: self, options: nil)?.first as? T else { // 3
// xib not loaded, or its top view is of the wrong type
return nil
}
self.addSubview(contentView) // 4
contentView.translatesAutoresizingMaskIntoConstraints = false // 5
contentView.layoutAttachAll(to: self) // 6
return contentView // 7
}
}
- Utiliser une valeur de retour jetable car la vue renvoyée n'a généralement aucun intérêt pour l'appelant lorsque toutes les prises sont déjà connectées.
- Il s'agit d'une méthode générique qui renvoie un objet facultatif de type UIView. S'il ne parvient pas à charger la vue, il renvoie nil.
- Tentative de chargement d'un fichier XIB portant le même nom que l'instance de classe actuelle. Si cela échoue, nil est renvoyé.
- Ajout de la vue de niveau supérieur à la hiérarchie des vues.
- Cette ligne suppose que nous utilisons des contraintes pour mettre en page la vue.
- Cette méthode ajoute des contraintes de haut, de bas, de début et de fin - en attachant la vue à «soi» de tous les côtés (voir: https://stackoverflow.com/a/46279424/2274829 pour plus de détails)
- Retour à la vue de niveau supérieur
Et la méthode de l'appelant pourrait ressembler à ceci:
final class SomeView: UIView { // 1.
required init?(coder aDecoder: NSCoder) { // 2 - storyboard initializer
super.init(coder: aDecoder)
fromNib() // 5.
}
init() { // 3 - programmatic initializer
super.init(frame: CGRect.zero) // 4.
fromNib() // 6.
}
// other methods ...
}
- SomeClass est une sous-classe UIView qui charge son contenu à partir d'un fichier SomeClass.xib. Le mot-clé "final" est facultatif.
- Un initialiseur lorsque la vue est utilisée dans un storyboard (n'oubliez pas d'utiliser SomeClass comme classe personnalisée de votre vue storyboard).
- Un initialiseur pour quand la vue est créée par programme (c'est-à-dire: "let myView = SomeView ()").
- Utilisation d'un cadre entièrement zéros puisque cette vue est mise en page à l'aide de la mise en page automatique. Notez qu'une méthode "init (frame: CGRect) {..}" n'est pas créée indépendamment, car la mise en page automatique est utilisée exclusivement dans notre projet.
- & 6. Chargement du fichier xib en utilisant l'extension.
Crédit: L'utilisation d'une extension générique dans cette solution a été inspirée par la réponse de Robert ci-dessous.
Modifier
Changer "vue" en "contentView" pour éviter toute confusion. Également changé l'indice du tableau en ".first".
File's Owner
atteigne l'endroit ... Merci!fromNib()
de l'intérieurinit(coder aDecoder: NSCoder)
crée une boucle infinie lorsque le chargement de la Nib à l'intérieur de lafromNib()
méthode appelle:init(coder aDecoder: NSCoder)
Ma contribution:
Alors appelez-le comme ceci:
..ou même:
la source
Le fait de pouvoir revenir
-> Self
rapidement permet de simplifier un peu cela. Dernière confirmation sur Swift 5.Si votre
.xib
fichier et votre sous-classe partagent le même nom, vous pouvez utiliser:Si vous avez un nom personnalisé, utilisez:
REMARQUE:
Si vous obtenez l'erreur "vue de type YourType introuvable dans ..", vous n'avez pas défini la classe de la vue dans le
.xib
fichierSélectionnez votre vue dans le
.xib
fichier, et appuyez surcmd + opt + 4
et dans l'class
entrée, entrez votre classela source
let myCustomView = UIView.fromNib() as? CustomView
. Dans ce cas,T.self
résoutUIView
plutôt queCustomView
et ne parvient pas à trouver la pointe. Je ne sais pas pourquoi c'est - peut-être le type inféré pour leslet
moyens que la fonction est appelée en tant queUIView
?essayez de suivre le code.
Éditer:
la source
Extensions de protocole Swift 4
Adoption
Cette implémentation suppose que le Nib porte le même nom que la classe UIView. Ex. MyView.xib. Vous pouvez modifier ce comportement en implémentant nibName () dans MyView pour renvoyer un nom différent de l'implémentation d'extension de protocole par défaut.
Dans le xib, le propriétaire des fichiers est MyView et la classe de vue racine est MyView.
Usage
la source
J'ai réalisé cela avec Swift par le code suivant:
N'oubliez pas de connecter votre prise de vue XIB pour voir la prise définie dans swift. Vous pouvez également définir First Responder sur votre nom de classe personnalisé pour commencer à connecter des prises supplémentaires.
J'espère que cela t'aides!
la source
Testé dans Xcode 7 beta 4, Swift 2.0 et iOS9 SDK. Le code suivant attribuera xib à uiview. Vous pouvez utiliser cette vue xib personnalisée dans le storyboard et accéder également à l'objet IBOutlet.
Accéder à la vue personnalisée par programme
Code source - https://github.com/karthikprabhuA/CustomXIBSwift
la source
Si vous avez beaucoup de vues personnalisées dans votre projet, vous pouvez créer une classe comme
UIViewFromNib
Swift 2.3
Swift 3
Et dans chaque classe juste héritée de
UIViewFromNib
, vous pouvez également remplacer lanibName
propriété si le.xib
fichier a un nom différent:la source
S'appuyant sur les solutions ci-dessus.
Cela fonctionnera sur tous les lots de projets et aucun besoin de génériques lors de l'appel de fromNib ().
Swift 2
Swift 3
Peut être utilisé comme ceci:
Ou comme ça:
la source
Swift 4
N'oubliez pas d'écrire ".first as? CustomView".
Si vous souhaitez utiliser n'importe où
La meilleure solution est la réponse de Robert Gummesson .
Alors appelez-le comme ceci:
la source
la source
self
est renvoyé implicitement de toutes lesinit
méthodes ...Une bonne façon de faire cela avec Swift est d'utiliser une énumération.
Ensuite, dans votre code, vous pouvez simplement utiliser:
la source
Je préfère cette solution (basée sur la réponse si @ GK100):
Dans SomeView.swift, j'ai chargé le XIB dans l' initialiseur
init
ouinit:frame: CGRect
. Il n'est pas nécessaire d'attribuer quoi que ce soit à «soi». Dès que le XIB est chargé, toutes les prises sont connectées, y compris la vue de niveau supérieur. La seule chose qui manque, c'est d'ajouter la vue de dessus à la hiérarchie des vues:la source
Version Swift 3 de la réponse de Logan
la source
Voici une manière propre et déclarative de charger par programme une vue à l'aide d'un protocole et d'une extension de protocole (Swift 4.2):
Et vous pouvez utiliser ceci comme ceci:
Quelques considérations supplémentaires :
Custom Class
ensemble de la vue (et les sorties / actions définies à partir de là), et non celui du propriétaire du fichier.la source
Je fais juste de cette façon:
Cet exemple utilise la première vue de la pointe "MyView.xib" dans le bundle principal. Mais vous pouvez faire varier l'index, le nom de la pointe ou le bundle (main par défaut).
J'avais l'habitude de réveiller des vues dans la méthode view init ou de créer des méthodes génériques comme dans les solutions ci-dessus (qui sont intelligentes d'ailleurs), mais je ne le fais plus.
De cette façon, je peux utiliser différentes dispositions ou caractéristiques tout en conservant la même logique de vue et le même code.
Je trouve plus facile de laisser un objet d'usine (généralement le viewController qui utilisera la vue) le créer comme il en a besoin. Parfois, vous avez besoin d'un propriétaire (généralement lorsque la vue créée est connectée au créateur), parfois non.
C'est probablement pourquoi Apple n'a pas inclus de
initFromNib
méthode dans sa classe UIView ...Pour prendre un exemple au niveau du sol, vous ne savez pas comment vous êtes né. Vous venez de naître. Ainsi sont les vues;)
la source
Tout ce que vous avez à faire est d'appeler la méthode init dans votre
UIView
classe.Faites-le de cette façon:
Maintenant, si vous souhaitez ajouter cette vue en tant que sous-vue dans le contrôleur de vue, faites-le de cette façon dans le fichier view controller.swift:
la source
Similaire à certaines des réponses ci-dessus mais une extension Swift3 UIView plus cohérente:
Ce qui donne la commodité de pouvoir charger la classe à partir d'une nib auto-nommée mais aussi à partir d'autres nibs / bundles.
la source
Vous pouvez le faire via le storyboard, ajoutez simplement les contraintes appropriées pour la vue. Vous pouvez le faire facilement en sous-classant n'importe quelle vue de votre choix, disons
BaseView
:Objectif c
Swift 4
Je propose 2 variantes pour ajouter des contraintes - commune et dans le langage de format visuel - sélectionnez celle que vous voulez :)
De plus, par défaut, on suppose que ce
xib
nom a le même nom que le nom de la classe d'implémentation. Si non, changez simplement lexibName
paramètre.Si vous sous-classez votre vue de
BaseView
- vous pouvez facilement placer n'importe quelle vue et spécifier la classe dans IB.la source
la source
Version plus puissante basée sur la réponse de Logan
Et vous pouvez utiliser comme
la source
La mise en œuvre la plus pratique. Ici, vous avez besoin de deux méthodes, afin de retourner directement à l'objet de votre classe, pas à UIView.
Exemple:
la source
Si vous voulez que la sous-classe Swift UIView soit entièrement autonome et puisse être instanciée en utilisant init ou init (frame :) sans exposer les détails d'implémentation de l'utilisation d'un Nib, alors vous pouvez utiliser une extension de protocole pour y parvenir. Cette solution évite la hiérarchie UIView imbriquée comme suggéré par de nombreuses autres solutions.
la source
la source
Je préfère l'extension ci-dessous
La différence entre cette extension et l'extension la plus répondue est que vous n'avez pas besoin de la stocker en tant que constante ou variable.
la source
la source