J'essaie de faire quelque chose d'un peu complexe, mais quelque chose qui devrait être possible. Voici donc un défi pour tous vos experts (ce forum regroupe beaucoup d'entre vous :)).
Je crée un "composant" Questionnaire que je souhaite charger sur un NavigationContoller
(mon QuestionManagerViewController
). Le "composant" est un "vide" UIViewController
, qui peut charger différentes vues en fonction de la question à laquelle il faut répondre.
La façon dont je le fais est:
UIView
, définissant des éléments IBOutlets
.Question1View.xib
(ICI IS O MON PROBLÈME EST PROBABLEMENT). Je règle à la fois le UIViewController
et le UIView
de classe Question1View.Je remplace le initWithNib
de mon QuestionManagerViewController
pour ressembler à ceci:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if (self = [super initWithNibName:@"Question1View" bundle:nibBundleOrNil]) {
// Custom initialization
}
return self;
}
Lorsque je lance le code, j'obtiens cette erreur:
2009-05-14 15: 05: 37.152 iMobiDines [17148: 20b] *** Application terminée en raison d'une exception non interceptée '
NSInternalInconsistencyException
', raison: '-[UIViewController _loadViewFromNibNamed:bundle:]
a chargé la plume "Question1View", mais la vue sortie n'a pas été réglé.
Je suis sûr qu'il existe un moyen de charger la vue à l'aide du fichier nib sans avoir à créer une classe viewController.
Il existe également un moyen plus simple d’accéder à la vue au lieu de traiter la nib en tant que tableau.
1) Créez une sous-classe View personnalisée avec les prises auxquelles vous souhaitez accéder ultérieurement. --Mon avis
2) dans l'UIViewController que vous voulez charger et gérer le nib, créez une propriété IBOutlet qui contiendra la vue du nib chargé, par exemple
dans MyViewController (une sous-classe UIViewController)
@property (nonatomic, retain) IBOutlet UIView *myViewFromNib;
(n'oubliez pas de le synthétiser et de le publier dans votre fichier .m)
) ouvrez votre nib (nous l'appellerons 'myViewNib.xib') dans IB, définissez le propriétaire de votre fichier sur MyViewController
4) connectez maintenant la sortie propriétaire de votre fichier myViewFromNib à la vue principale de la nib.
5) Maintenant dans MyViewController, écrivez la ligne suivante:
[[NSBundle mainBundle] loadNibNamed:@"myViewNib" owner:self options:nil];
Désormais, en appelant votre propriété "self.myViewFromNib", vous aurez accès à la vue depuis votre nib!
Merci à tous. J'ai trouvé un moyen de faire ce que je voulais.
UIView
avec le IBOutlet
s dont vous avez besoin.UIViewController
(aucune sous-classe personnalisée, mais la "vraie"). La vue du propriétaire du fichier est connectée à la vue principale et sa classe est déclarée comme étant celle de l'étape 1).IBOutlet
s.La DynamicViewController
peut exécuter sa logique pour décider quelle vue/xib charger. Une fois sa décision prise, dans la méthode loadView
, mettez quelque chose comme ceci:
NSArray* nibViews = [[NSBundle mainBundle] loadNibNamed:@"QPickOneView"
owner:self
options:nil];
QPickOneView* myView = [ nibViews objectAtIndex: 1];
myView.question = question;
C'est ça!
La méthode loadNibNamed
du paquet principal se charge d'initialiser la vue et de créer les connexions.
Maintenant, le ViewController peut afficher une vue ou une autre en fonction des données en mémoire, et l'écran "parent" n'a pas besoin de se soucier de cette logique.
Je ne suis pas sûr de ce que certaines des réponses parlent, mais je dois mettre cette réponse ici pour ma prochaine recherche dans Google. Mots-clés: "Comment charger un UIView depuis un nib" ou "Comment charger un UIView depuis un NSBundle."
Voici le code presque 100% directement depuis Apress début iPhone Livre (page 247, "Utilisation de la nouvelle cellule du tableau"):
- (void)viewDidLoad {
[super viewDidLoad];
NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:@"Blah"
owner:self options:nil];
Blah *blah;
for (id object in bundle) {
if ([object isKindOfClass:[Blah class]]) {
blah = (Blah *)object;
break;
}
}
assert(blah != nil && "blah can't be nil");
[self.view addSubview: blah];
}
Cela suppose que vous avez une sous-classe UIView
appelée Blah
, une pointe appelée Blah
contenant un UIView
dont la classe est définie sur Blah
.
#import "NSObject+LoadFromNib.h"
@implementation NSObject (LoadFromNib)
+ (id)loadFromNib:(NSString *)name classToLoad:(Class)classToLoad {
NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:name owner:self options:nil];
for (id object in bundle) {
if ([object isKindOfClass:classToLoad]) {
return object;
}
}
return nil;
}
@end
extension UIView {
class func loadFromNib<T>(withName nibName: String) -> T? {
let nib = UINib.init(nibName: nibName, bundle: nil)
let nibObjects = nib.instantiate(withOwner: nil, options: nil)
for object in nibObjects {
if let result = object as? T {
return result
}
}
return nil
}
}
Et un exemple d'utilisation:
class SomeView: UIView {
class func loadFromNib() -> SomeView? {
return self.loadFromNib(withName: "SomeView")
}
}
Pour tous ceux qui ont besoin de gérer plus d'une instance de la vue personnalisée, il s'agit d'une Outlet Collection , j'ai fusionné et personnalisé les @Gonso, @AVeryDev. et @Olie répond de cette manière:
Créez un MyView : UIView
personnalisé et définissez-le comme " Classe personnalisée " de la racine UIView
dans l'élément souhaité . _ xib _ ;
Créez toutes les sorties dont vous avez besoin dans MyView
(faites-le maintenant car après le point 3, l'IB vous proposera de connecter des sorties à la UIViewController
et non à la vue personnalisée que nous voulons);
Définissez votre UIViewController
comme " Propriétaire du fichier " de la vue personnalisée XIB;
Dans le UIViewController
ajoutez un nouveau UIViews
pour chaque instance de MyView
souhaitée et connectez-les à UIViewController
en créant une collection de prises : ces vues agiront comme des vues "enveloppantes" pour les instances de vue personnalisées;
Enfin, dans la viewDidLoad
de votre UIViewController
ajoutez les lignes suivantes:
NSArray *bundleObjects; MyView *currView; NSMutableArray *myViews = [NSMutableArray arrayWithCapacity:myWrapperViews.count]; for (UIView *currWrapperView in myWrapperViews) { bundleObjects = [[NSBundle mainBundle] loadNibNamed:@"MyView" owner:self options:nil]; for (id object in bundleObjects) { if ([object isKindOfClass:[MyView class]]){ currView = (MyView *)object; break; } } [currView.myLabel setText:@"myText"]; [currView.myButton setTitle:@"myTitle" forState:UIControlStateNormal]; //... [currWrapperView addSubview:currView]; [myViews addObject:currView]; } //self.myViews = myViews; if need to access them later..
J'utiliserais UINib pour instancier une UIView personnalisée pour qu'elle soit réutilisée
UINib *customNib = [UINib nibWithNibName:@"MyCustomView" bundle:nil];
MyCustomViewClass *customView = [[customNib instantiateWithOwner:self options:nil] objectAtIndex:0];
[self.view addSubview:customView];
Les fichiers nécessaires dans ce cas sont MyCustomView.xib, MyCustomViewClass.h et MyCustomViewClass.m Notez que [UINib instantiateWithOwner]
renvoie un tableau, donc vous devez utiliser l'élément qui reflète UIView que vous souhaitez réutiliser. Dans ce cas, c'est le premier élément.
Vous ne devez pas définir la classe de votre contrôleur de vue comme une sous-classe de UIView
dans Interface Builder. C'est très certainement au moins une partie de votre problème. Laissez-la soit comme UIViewController
, soit une sous-classe de celle-ci, ou une autre classe personnalisée que vous avez.
En ce qui concerne le chargement d’une vue à partir d’un fichier xib, j’étais supposé que vous aviez d’avoir une sorte de contrôleur de vue (même s’il n’étend pas UIViewController
, ce qui est peut-être trop poids lourd pour vos besoins) défini comme propriétaire du fichier dans Interface Builder si vous souhaitez l'utiliser pour définir votre interface. J'ai aussi fait quelques recherches pour le confirmer. En effet, sinon, il n’y aurait aucun moyen d’accéder aux éléments d’interface de la variable UIView
et il n’y aurait pas non plus moyen de faire en sorte que vos propres méthodes dans le code soient déclenchées par des événements.
Si vous utilisez un UIViewController
comme propriétaire de votre fichier pour vos vues, vous pouvez simplement utiliser initWithNibName:bundle:
pour le charger et récupérer l'objet du contrôleur de vue. Dans IB, assurez-vous de définir la sortie view
sur la vue avec votre interface dans le fichier xib. Si vous utilisez un autre type d'objet en tant que propriétaire de votre fichier, vous devez utiliser la méthode loadNibNamed:owner:options:
de NSBundle
pour charger le nib, en transmettant une instance de File's Owner à la méthode. Toutes ses propriétés seront définies correctement en fonction des prises que vous définissez dans IB.
Vous pouvez également utiliser initWithNibName de UIViewController au lieu de loadNibNamed. C'est plus simple, je trouve.
UIViewController *aViewController = [[UIViewController alloc] initWithNibName:@"MySubView" bundle:nil];
[self.subview addSubview:aViewController.view];
[aViewController release]; // release the VC
Il ne vous reste plus qu'à créer MySubView.xib et MySubView.h/m. Dans MySubView.xib, définissez la classe Propriétaire du fichier sur UIViewController et affichez la classe sur MySubView.
Vous pouvez positionner et la taille de subview
à l'aide du fichier xib parent.
C'est quelque chose qui devrait être plus facile. J'ai fini par étendre UIViewController
et ajouter un sélecteur loadNib:inPlaceholder:
. Maintenant je peux dire
self.mySubview = (MyView *)[self loadNib:@"MyView" inPlaceholder:mySubview];
Voici le code pour la catégorie (il fait le même rigamarole que décrit par Gonso):
@interface UIViewController (nibSubviews)
- (UIView *)viewFromNib:(NSString *)nibName;
- (UIView *)loadNib:(NSString *)nibName inPlaceholder:(UIView *)placeholder;
@end
@implementation UIViewController (nibSubviews)
- (UIView *)viewFromNib:(NSString *)nibName
{
NSArray *xib = [[NSBundle mainBundle] loadNibNamed:nibName owner:self options:nil];
for (id view in xib) { // have to iterate; index varies
if ([view isKindOfClass:[UIView class]]) return view;
}
return nil;
}
- (UIView *)loadNib:(NSString *)nibName inPlaceholder:(UIView *)placeholder
{
UIView *nibView = [self viewFromNib:nibName];
[nibView setFrame:placeholder.frame];
[self.view insertSubview:nibView aboveSubview:placeholder];
[placeholder removeFromSuperview];
return nibView;
}
@end
C’est une excellente question (+1) et les réponses étaient presque utile;) Désolé les gars, mais j’ai passé un sacré moment à passer à travers cela, même si Gonso et AVeryDev ont tous deux donné de bons conseils. Espérons que cette réponse aidera les autres.
MyVC
est le contrôleur de vue contenant tout cela.
MySubview
est la vue que nous voulons charger depuis un xib
MySubView
ayant la bonne taille, la bonne forme et la position souhaitée.Dans MyVC.h, ont
IBOutlet MySubview *mySubView
// ...
@property (nonatomic, retain) MySubview *mySubview;
Dans MyVC.m, @synthesize mySubView;
et n'oubliez pas de le publier dans dealloc
.
UIView *view
(peut être inutile, mais cela a fonctionné pour moi.) Synthétisez et publiez-le au format .mMySubview
et liez la propriété view à votre vue.IBOutlet
De retour dans MyVC.m, ont
NSArray *xibviews = [[NSBundle mainBundle] loadNibNamed: @"MySubview" owner: mySubview options: NULL];
MySubview *msView = [xibviews objectAtIndex: 0];
msView.frame = mySubview.frame;
UIView *oldView = mySubview;
// Too simple: [self.view insertSubview: msView aboveSubview: mySubview];
[[mySubview superview] insertSubview: msView aboveSubview: mySubview]; // allows nesting
self.mySubview = msView;
[oldCBView removeFromSuperview];
Le problème le plus difficile pour moi était le suivant: les astuces des autres réponses chargeaient ma vue de xib, mais ne remplaçaient PAS celle de MyVC (duh!) - je devais la changer moi-même.
De plus, pour accéder aux méthodes de mySubview
, la propriété view
du fichier .xib doit être définie sur MySubview
. Sinon, cela revient comme un UIView
ordinaire.
S'il y a un moyen de charger mySubview
directement à partir de sa propre xib, c'est génial, mais cela me conduit là où je devais être.
Moi aussi je voulais faire quelque chose de similaire, voici ce que j'ai trouvé: (SDK 3.1.3)
J'ai un contrôleur de vue A (lui-même appartenant à un contrôleur de navigation) qui charge VC B en appuyant sur un bouton:
Dans AViewController.m
BViewController *bController = [[BViewController alloc] initWithNibName:@"Bnib" bundle:nil];
[self.navigationController pushViewController:bController animated:YES];
[bController release];
Maintenant, VCB a son interface depuis Bnib, mais lorsqu'un bouton est enfoncé, je souhaite passer à un 'mode édition' disposant d'une interface distincte, mais je ne souhaite pas changer de mode. VC pour le mode édition, je souhaite que la nouvelle plume soit associée à mon VC existant.
Donc, dans BViewController.m (dans la méthode de pression de bouton)
NSArray *nibObjects = [[NSBundle mainBundle] loadNibNamed:@"EditMode" owner:self options:nil];
UIView *theEditView = [nibObjects objectAtIndex:0];
self.editView = theEditView;
[self.view addSubview:theEditView];
Puis, sur un autre bouton, appuyez sur (pour quitter le mode édition):
[editView removeFromSuperview];
et je reviens à mon original Bnib.
Cela fonctionne bien, mais notez que mon EditMode.nib ne contient qu’un seul obj de niveau supérieur, un obj UIView. Peu importe que le propriétaire du fichier dans ce nib soit défini en tant que BViewController ou que NSObject par défaut, MAIS assurez-vous que la vue View Outlet dans le propriétaire du fichier N'EST PAS définie. Si c'est le cas, alors je reçois un crash exc_bad_access et xcode continue à charger 6677 cadres de pile montrant une méthode UIView interne appelée à plusieurs reprises ... ressemble donc à une boucle infinie. (Le View Outlet IS défini dans mon Bnib original cependant)
J'espère que cela t'aides.
dans Swift
En fait, ma solution à ce problème était de charger la vue dans un viewDidLoad dans mon CustonViewController où je voulais utiliser la vue comme ça:
myAccessoryView = NSBundle.mainBundle().loadNibNamed("MyAccessoryView", owner: self, options: nil)[0] as! MyAccessoryView
Ne chargez pas la vue dans une méthode loadView()
! La méthode loadView sert au chargement de la vue pour votre ViewController personnalisé.
J'ai fait une catégorie que j'aime bien:
UIView+NibInitializer.h
#import <UIKit/UIKit.h>
@interface UIView (NibInitializer)
- (instancetype)initWithNibNamed:(NSString *)nibNameOrNil;
@end
UIView+NibInitializer.m
#import "UIView+NibInitializer.h"
@implementation UIView (NibInitializer)
- (instancetype)initWithNibNamed:(NSString *)nibNameOrNil
{
if (!nibNameOrNil) {
nibNameOrNil = NSStringFromClass([self class]);
}
NSArray *viewsInNib = [[NSBundle mainBundle] loadNibNamed:nibNameOrNil
owner:self
options:nil];
for (id view in viewsInNib) {
if ([view isKindOfClass:[self class]]) {
self = view;
break;
}
}
return self;
}
@end
Ensuite, appelez comme ceci:
MyCustomView *myCustomView = [[MyCustomView alloc] initWithNibNamed:nil];
Utilisez un nom de plume si votre plume porte un nom autre que le nom de votre classe.
Pour le remplacer dans vos sous-classes pour un comportement supplémentaire, cela pourrait ressembler à ceci:
- (instancetype)initWithNibNamed:(NSString *)nibNameOrNil
{
self = [super initWithNibNamed:nibNameOrNil];
if (self) {
self.layer.cornerRadius = CGRectGetHeight(self.bounds) / 2.0;
}
return self;
}
Pour Swift utilisateur avec option configurable:
UIView
personnalisée et un fichier xib , que nous nommerons d'après notre propre nom de classe: dans notre cas, MemeView. Dans la classe Meme View, n'oubliez pas de la définir comme désignable avec l'attribut @IBDesignable
avant la déclaration de classe.Rappelez-vous de définir le propriétaire du fichier dans xib avec notre sous-classe personnalisée UIView
dans le panneau Inspecteur Indetity
Dans le fichier xib, nous pouvons maintenant construire notre interface, créer des contraintes, créer des points de vente, des actions, etc.
Nous devons implémenter quelques méthodes dans notre classe personnalisée pour ouvrir le fichier xib une fois initialisé.
class XibbedView: UIView {
weak var nibView: UIView!
override convenience init(frame: CGRect) {
let nibName = NSStringFromClass(self.dynamicType).componentsSeparatedByString(".").last!
self.init(nibName: nibName)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
let nibName = NSStringFromClass(self.dynamicType).componentsSeparatedByString(".").last!
let nib = loadNib(nibName)
nib.frame = bounds
nib.translatesAutoresizingMaskIntoConstraints = false
addSubview(nib)
nibView = nib
setUpConstraints()
}
init(nibName: String) {
super.init(frame: CGRectZero)
let nibName = NSStringFromClass(self.dynamicType).componentsSeparatedByString(".").last!
let nib = loadNib(nibName)
nib.frame = bounds
nib.translatesAutoresizingMaskIntoConstraints = false
addSubview(nib)
nibView = nib
setUpConstraints()
}
func setUpConstraints() {
["V","H"].forEach { (quote) -> () in
let format = String(format:"\(quote):|[nibView]|")
addConstraints(NSLayoutConstraint.constraintsWithVisualFormat(format, options: [], metrics: nil, views: ["nibView" : nibView]))
}
}
func loadNib(name: String) -> UIView {
let bundle = NSBundle(forClass: self.dynamicType)
let nib = UINib(nibName: name, bundle: bundle)
let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView
return view
}
}
Dans notre classe personnalisée, nous pouvons également définir certaines propriétés pouvant être inspectées pour les contrôler entièrement depuis le constructeur d'interface.
@IBDesignable class MemeView: XibbedView {
@IBInspectable var memeImage: UIImage = UIImage() {
didSet {
imageView.image = memeImage
}
}
@IBInspectable var textColor: UIColor = UIColor.whiteColor() {
didSet {
label.textColor = textColor
}
}
@IBInspectable var text: String = "" {
didSet {
label.text = text
}
}
@IBInspectable var roundedCorners: Bool = false {
didSet {
if roundedCorners {
layer.cornerRadius = 20.0
clipsToBounds = true
}
else {
layer.cornerRadius = 0.0
clipsToBounds = false
}
}
}
@IBOutlet weak var label: UILabel!
@IBOutlet weak var imageView: UIImageView!
}
Si nous avons besoin d'ajouter plus d'informations si la vue est affichée dans un storyboard ou un autre xib, nous pouvons pour cela implémenter prepareForInterfaceBuilder()
, mais cette méthode ne sera exécutée que lors de l'ouverture du fichier dans le générateur d'interface. Si vous avez fait tout ce que j'ai écrit mais que rien ne fonctionne, c'est un moyen de déboguer une vue sigle en ajoutant des points d'arrêt dans son implémentation.
Voici la hiérarchie des vues.
J'espère que cela vous aidera à télécharger un échantillon complet ici
J'ai trouvé cette publication sur le blog par Aaron Hillegass (auteur, instructeur, Cocoa ninja) très éclairant. Même si vous n'adoptez pas son approche modifiée pour charger les fichiers NIB via un initialiseur désigné, vous aurez probablement au moins une meilleure compréhension du processus en cours. J'utilise cette méthode récemment avec beaucoup de succès!
J'avais des raisons de faire la même chose (chargement par programme d'une vue à partir d'un fichier XIB), mais je devais le faire entièrement à partir du contexte d'une sous-classe d'une sous-classe d'un UIView
(c'est-à-dire sans impliquer le contrôleur de vue dans en tous cas). Pour ce faire, j'ai créé cette méthode utilitaire:
+ (id) initWithNibName:(NSString *)nibName withSelf:(id)myself {
NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:nibName
owner:myself options:nil];
for (id object in bundle) {
if ([object isKindOfClass:[myself class]]) {
return object;
}
}
return nil;
}
Ensuite, je l’appelle depuis ma sous-classe 'initWithFrame
comme ceci:
- (id)initWithFrame:(CGRect)frame {
self = [Utilities initWithNibName:@"XIB1" withSelf:self];
if (self) {
// Initialization code.
}
return self;
}
Posté pour l'intérêt général; si quelqu'un voit des problèmes sans le faire de cette façon, s'il vous plaît faites le moi savoir.
Voici un moyen de le faire dans Swift (en train d'écrire Swift 2.0 dans XCode 7 beta 5).
Depuis votre sous-classe UIView
que vous avez définie comme "Classe personnalisée" dans le Générateur d'interface, créez une méthode comme celle-ci (ma sous-classe est appelée RecordingFooterView):
class func loadFromNib() -> RecordingFooterView? {
let nib = UINib(nibName: "RecordingFooterView", bundle: nil)
let nibObjects = nib.instantiateWithOwner(nil, options: nil)
if nibObjects.count > 0 {
let topObject = nibObjects[0]
return topObject as? RecordingFooterView
}
return nil
}
Ensuite, vous pouvez simplement l'appeler comme ceci:
let recordingFooterView = RecordingFooterView.loadFromNib()
La réponse précédente ne prend pas en compte un changement de la structure NIB (XIB) survenu entre la version 2.0 et la version 2.1 du kit SDK pour iPhone. Le contenu de l'utilisateur commence maintenant à l'index 0 au lieu de 1.
Vous pouvez utiliser la macro 2.1 valable pour toutes les versions 2.1 et supérieures (deux soulignements avant IPHONE:
// Cited from previous example
NSArray* nibViews = [[NSBundle mainBundle] loadNibNamed:@"QPickOneView" owner:self options:nil];
int startIndex;
#ifdef __IPHONE_2_1
startIndex = 0;
#else
startIndex = 1;
#endif
QPickOneView* myView = [ nibViews objectAtIndex: startIndex];
myView.question = question;
Nous utilisons une technique similaire à celle-ci pour la plupart de nos applications.
Barney
Aucune des réponses n’explique comment créer le fichier XIB autonome à la base de cette question. Il n'y a pas d'option Xcode 4
pour "Créer un nouveau fichier XIB".
Pour faire ça
1) Choose "New File..."
2) Choose the "User Interface" category under the iOS section
3) Choose the "View" item
4) You will then be prompted to choose an iPhone or iPad format
Cela peut sembler simple mais cela peut vous éviter quelques minutes de fouilles car le mot "XIB" n'apparaît nulle part.
@AVeryDev
6) Pour attacher la vue chargée à la vue de votre contrôleur de vue:
[self.view addSubview:myViewFromNib];
Vraisemblablement, il est nécessaire de le supprimer de la vue pour éviter les fuites de mémoire.
Pour clarifier: le contrôleur de vue a plusieurs IBOutlets, dont certains sont connectés aux éléments du fichier nib d'origine (comme d'habitude) et d'autres à des éléments du nib chargé. Les deux nibs ont la même classe de propriétaire. La vue chargée recouvre celle d'origine.
Astuce: définissez l'opacité de la vue principale dans la plume chargée sur zéro afin qu'elle ne masque pas les éléments de la plume d'origine.
Version la plus courte:
RSFavoritePlaceholderView *favoritePlaceholderView = [[[NSBundle mainBundle] loadNibNamed:@"RSFavoritePlaceholderView" owner:self options:nil] firstObject];
Après avoir passé de nombreuses heures, j'ai forgé la solution suivante. Suivez ces étapes pour créer un UIView
personnalisé.
1) Créer une classe myCustomView héritée de UIView .
2) Créez . Xib avec le nom myCustomView .
3) Modifiez la classe de UIView dans votre fichier .xib, attribuez myCustomView Classe là-bas.
5) Chargez .xib dans myCustomView * customView . Utilisez l'exemple de code suivant.
myCustomView * myView = [[[NSBundle mainBundle] loadNibNamed:@"myCustomView" owner:self options:nil] objectAtIndex:0];
[myView.description setText:@"description"];
Remarque: pour ceux qui ont toujours des problèmes à commenter, je leur fournirai un lien vers un exemple de projet avec myCustomView
J'ai l'habitude de nommer xibs avec des vues identiques à la vue. Comme on le ferait pour un contrôleur de vue. Ensuite, je n'ai pas à écrire les noms de classe dans le code. Je charge un UIView depuis un fichier nib portant le même nom.
Exemple pour une classe appelée MyView.
Dans votre code, créez un nouveau MyView comme ceci:
MyView * myView = [MyView nib_viewFromNibWithOwner: propriétaire];
Voici la catégorie pour cela:
@implementation UIView (nib)
+ (id) nib_viewFromNib {
return [self nib_viewFromNibWithOwner:nil];
}
+ (id) nib_viewFromNibWithOwner:(id)owner {
NSString *className = NSStringFromClass([self class]);
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:className owner:owner options:nil];
UIView *view = nil;
for(UIView *v in nib) {
if ([v isKindOfClass:[self class]]) {
view = v;
break;
}
}
assert(view != nil && "View for class not found in nib file");
[view nib_viewDidLoad];
return view;
}
// override this to do custom setup
-(void)nib_viewDidLoad {
}
Ensuite, je câblais des boutons avec les actions du contrôleur que j'utilise et définissais les choses sur les étiquettes à l'aide des prises de ma sous-classe de vues personnalisées.
Pour charger par programme une vue depuis un nib/xib dans Swift 4:
// Load a view from a Nib given a placeholder view subclass
// Placeholder is an instance of the view to load. Placeholder is discarded.
// If no name is provided, the Nib name is the same as the subclass type name
//
public func loadViewFromNib<T>(placeholder placeholderView: T, name givenNibName: String? = nil) -> T {
let nib = loadNib(givenNibName, placeholder: placeholderView)
return instantiateView(fromNib: nib, placeholder: placeholderView)
}
// Step 1: Returns a Nib
//
public func loadNib<T>(_ givenNibName: String? = nil, placeholder placeholderView: T) -> UINib {
//1. Load and unarchive nib file
let nibName = givenNibName ?? String(describing: type(of: placeholderView))
let nib = UINib(nibName: nibName, bundle: Bundle.main)
return nib
}
// Step 2: Instantiate a view given a nib
//
public func instantiateView<T>(fromNib nib: UINib, placeholder placeholderView: T) -> T {
//1. Get top level objects
let topLevelObjects = nib.instantiate(withOwner: placeholderView, options: nil)
//2. Have at least one top level object
guard let firstObject = topLevelObjects.first else {
fatalError("\(#function): no top level objects in nib")
}
//3. Return instantiated view, placeholderView is not used
let instantiatedView = firstObject as! T
return instantiatedView
}