Je suis conscient que cette question a déjà été posée, mais les réponses sont contradictoires et je suis confus, alors s'il vous plaît, ne m'embrassez pas.
Je souhaite avoir une sous-classe réutilisable UIView
dans mon application. Je veux décrire l'interface en utilisant un fichier nib.
Maintenant, supposons qu'il s'agisse d'une vue d'indicateur de chargement contenant un indicateur d'activité. Je voudrais qu'un événement instancie cette vue et l'anime dans la vue d'un contrôleur de vue. Je pourrais décrire l'interface de la vue sans problème par programmation, en créant les éléments par programmation et en définissant leur cadre dans une méthode init, etc.
Comment puis-je faire cela en utilisant une plume cependant? Conserver la taille donnée dans le constructeur d'interface sans avoir à définir de cadre.
J'ai réussi à le faire comme ça, mais je suis sûr que c'est faux (c'est juste une vue avec un sélecteur dedans):
- (id)initWithDataSource:(NSDictionary *)dataSource {
self = [super init];
if (self){
self = [[[NSBundle mainBundle] loadNibNamed:[NSString stringWithFormat:@"%@", [self class]] owner:self options:nil] objectAtIndex:0];
self.pickerViewData = dataSource;
[self configurePickerView];
}
return self;
}
Mais je m'écrase moi-même et quand je l'instancie:
FSASelectView *selectView = [[FSASelectView alloc] initWithDataSource:selectViewDictionary];
selectView.delegate = self;
selectView.frame = CGRectMake(0, self.view.bottom + 50, [FSASelectView width], [FSASelectView height]);
Je dois définir manuellement le cadre plutôt que de le récupérer auprès de IB.
EDIT: je veux créer cette vue personnalisée dans un contrôleur de vue et avoir accès au contrôle des éléments de la vue. Je ne veux pas d'un nouveau contrôleur de vue.
Merci
EDIT: Je ne sais pas si c'est la meilleure pratique, je suis sûr que ce n'est pas, mais c'est comme ça que je l'ai fait:
FSASelectView *selectView = [[[NSBundle mainBundle] loadNibNamed:[NSString stringWithFormat:@"%@",[FSASelectView class]] owner:self options:nil] objectAtIndex:0];
selectView.delegate = self;
[selectView configurePickerViewWithData:ds];
selectView.frame = CGRectMake(0, self.view.bottom + 50, selectView.width, selectView.height);
selectView.alpha = 0.9;
[self.view addSubview:selectView];
[UIView animateWithDuration: 0.25 delay: 0 options:UIViewAnimationOptionAllowUserInteraction |UIViewAnimationOptionCurveEaseInOut animations:^{
selectView.frame = CGRectMake(0, self.view.bottom - selectView.height, selectView.width, selectView.height);
selectView.alpha = 1;
} completion:^(BOOL finished) {
}];
Bonne pratique toujours recherchée
Est-ce que cela aurait dû être fait en utilisant un contrôleur de vue et init avec nom de nib? Devrais-je avoir défini la nib dans une méthode d'initialisation UIView dans le code? Ou est ce que j'ai fait ok?
MyViewClass *myViewObject = [[[NSBundle mainBundle] loadNibNamed:@"MyViewClassNib" owner:self options:nil] objectAtIndex:0]
J'utilise ceci pour initialiser les vues personnalisées réutilisables que j'ai.
Notez que vous pouvez utiliser "firstObject" à la fin, c'est un peu plus propre. "firstObject" est une méthode pratique pour NSArray et NSMutableArray.
Voici un exemple typique de chargement d'un xib à utiliser comme en-tête de table. Dans votre dossier YourClass.m
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
return [[NSBundle mainBundle] loadNibNamed:@"TopArea" owner:self options:nil].firstObject;
}
Normalement, dans le TopArea.xib
, vous cliquez sur le propriétaire du fichier et définissez le propriétaire du fichier sur YourClass. Alors en fait dans YourClass.h vous auriez des propriétés IBOutlet. Dans TopArea.xib
, vous pouvez faire glisser les contrôles vers ces prises.
N'oubliez pas que dans TopArea.xib
, vous devrez peut-être cliquez sur la vue elle-même et faites-la glisser vers un point de vente, de sorte que vous en ayez le contrôle, si nécessaire. (Un conseil très intéressant est que lorsque vous effectuez cette opération pour les lignes de cellules de tableau, vous devez absolument le faire - vous devez connecter la vue elle-même à la propriété correspondante dans votre code.)
Si vous voulez garder votre CustomView
et ses xib
indépendamment de File's Owner
, puis suivez ces étapes
File's Owner
champ vide.xib
de votre CustomView
et définissez-la Custom Class
comme CustomView
(nom de votre classe de vue personnalisée)IBOutlet
dans .h
fichier de votre vue personnalisée..xib
fichier de votre vue personnalisée, cliquez dessus et allez dans Connection Inspector
. Vous y trouverez tous vos IBOutlets que vous définissez dans .h
fichierdans .m
fichier de votre classe CustomView
, remplacez la méthode init
comme suit
-(CustomView *) init{
CustomView *result = nil;
NSArray* elements = [[NSBundle mainBundle] loadNibNamed: NSStringFromClass([self class]) owner:self options: nil];
for (id anObject in elements)
{
if ([anObject isKindOfClass:[self class]])
{
result = anObject;
break;
}
}
return result;
}
Maintenant, lorsque vous voulez charger votre CustomView
, utilisez la ligne de code suivante [[CustomView alloc] init];
Suivez les étapes suivantes
UIView
.MyView.xib
.UIViewController
de NSObject
dans xib. Voir l'image ci-dessous Connectez la vue du propriétaire du fichier à votre vue. Voir l'image ci-dessous
Changez la classe de votre vue en MyView
. Identique à 3.
Voici le code pour charger la vue:
UIViewController *controller=[[UIViewController alloc] initWithNibName:@"MyView" bundle:nil];
MyView* view=(MyView*)controller.view;
[self.view addSubview:myview];
J'espère que ça aide.
Clarification :
UIViewController
est utilisé pour charger votre xib et la vue que le UIViewController
a est en fait MyView
que vous avez assignée dans le xib MyView.
Démo J'ai créé une capture de démo ici
Répondant à ma propre question à propos de 2 ans ou quelque chose après, mais ...
Il utilise une extension de protocole afin que vous puissiez le faire sans aucun code supplémentaire pour toutes les classes.
/*
Prerequisites
-------------
- In IB set the view's class to the type hook up any IBOutlets
- In IB ensure the file's owner is blank
*/
public protocol CreatedFromNib {
static func createFromNib() -> Self?
static func nibName() -> String?
}
extension UIView: CreatedFromNib { }
public extension CreatedFromNib where Self: UIView {
public static func createFromNib() -> Self? {
guard let nibName = nibName() else { return nil }
guard let view = NSBundle.mainBundle().loadNibNamed(nibName, owner: nil, options: nil).last as? Self else { return nil }
return view
}
public static func nibName() -> String? {
guard let n = NSStringFromClass(Self.self).componentsSeparatedByString(".").last else { return nil }
return n
}
}
// Usage:
let myView = MyView().createFromNib()
En rapide:
Par exemple, le nom de votre classe personnalisée est InfoView
Au début, vous créez des fichiers InfoView.xib
et InfoView.Swift
comme ça:
import Foundation
import UIKit
class InfoView: UIView {
class func instanceFromNib() -> UIView {
return UINib(nibName: "InfoView", bundle: nil).instantiateWithOwner(nil, options: nil)[0] as! UIView
}
Puis définissez File's Owner
à UIViewController
comme ceci:
Renommez votre View
en InfoView
:
Faites un clic droit sur File's Owner
et connectez votre champ view
à votre InfoView
:
Assurez-vous que le nom de la classe est InfoView
:
Et après cela, vous pouvez ajouter l'action au bouton dans votre classe personnalisée sans aucun problème:
Et l'utilisation de cette classe personnalisée dans votre MainViewController
:
func someMethod() {
var v = InfoView.instanceFromNib()
v.frame = self.view.bounds
self.view.addSubview(v)
}
Vous pouvez également initialiser le fichier xib à l’aide d’un contrôleur de vue et utiliser viewController.view. ou faites-le comme vous l'avez fait. Créer une sous-classe UIView
en tant que contrôleur pour UIView
est une mauvaise idée.
Si vous ne disposez d'aucun point de vente dans votre vue personnalisée, vous pouvez directement utiliser une classe UIViewController
pour l'initialiser.
Mise à jour: Dans votre cas:
UIViewController *genericViewCon = [[UIViewController alloc] initWithNibName:@"CustomView"];
//Assuming you have a reference for the activity indicator in your custom view class
CustomView *myView = (CustomView *)genericViewCon.view;
[parentView addSubview:myView];
//And when necessary
[myView.activityIndicator startAnimating]; //or stop
Sinon, vous devez créer un UIViewController
personnalisé (pour en faire le propriétaire du fichier afin que les sorties soient correctement câblées).
YourCustomController *yCustCon = [[YourCustomController alloc] initWithNibName:@"YourXibName"].
Où que vous souhaitiez ajouter la vue, vous pouvez l'utiliser.
[parentView addSubview:yCustCon.view];
Cependant, passer le contrôleur d’une autre vue (déjà utilisé pour une autre vue) en tant que propriétaire lors du chargement de la xib n’est pas une bonne idée, car la propriété view du contrôleur sera modifiée et vous ne pourrez pas accéder à la vue originale. avoir une référence à elle.
EDIT: Vous devrez faire face à ce problème si vous avez configuré votre nouvelle xib avec le propriétaire du fichier comme étant la même classe principale UIViewController
et lié la propriété view à la nouvelle vue xib.
c'est à dire;
Le code ci-dessous causera de la confusion plus tard, si vous l'écrivez dans la vue, le chargement de YourMainViewController
. C'est parce que self.view
à partir de ce moment-là, on fera référence à votre vue personnalisée
-(void)viewDidLoad:(){
UIView *childView= [[[NSBundle mainBundle] loadNibNamed:@"YourXibName" owner:self options:nil] objectAtIndex:0];
}